刷刷笔试题~~(6)[C++]

1.以下程序输出结果是____
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class A
{
   public :
     A ():m_iVal(0){test();}
     virtual void func() { std::cout<<m_iVal<<‘ ’;}
     void test(){func();}
   public :
       int m_iVal;
};
class B :  public A
{
   public :
     B(){test();}
     virtual void func()
     {
       ++m_iVal;
       std::cout<<m_iVal<<‘ ’;
       }
};
int main( int argc , char * argv[])
{
   A*p =  new B;
   p->test();
   return 0;
}
正确答案: C 


A.1 0
B.0 1
C.0 1 2
D.2 1 0
E.不可预期
F.以上都不对
解析:
本问题涉及到两个方面:
1.C++继承体系中构造函数的调用顺序。
2.构造函数中调用虚函数问题。

C++继承体系中,初始化时构造函数的调用顺序如下
(1)任何虚拟基类的构造函数按照他们被继承的顺序构造
(2)任何非虚拟基类的构造函数按照他们被继承的顺序构造
(3)任何成员对象的函数按照他们声明的顺序构造
(4)类自己的构造函数
据此可知 A*p = newB;先调用A类的构造函数再调用B类的构造函数。

构造函数中调用虚函数,虚函数表现为该类中虚函数的行为,
即在父类构造函数中调用虚函数,虚函数的表现就是父类定义的函数的表现。why?原因如下:
假设构造函数中调用虚函数,表现为普通的虚函数调用行为,即虚函数会表现为相应的子类函数行为,并且假设子类存在一个成员变量int a;子类定义的虚函数的新的行为会操作a变量,在子类初始化时根据构造函数调用顺序会首先调用父类构造函数,那么虚函数回去操作a,而因为a是子类成员变量,这时a尚未初始化,这是一种危险的行为,作为一种明智的选择应该禁止这种行为。所以虚函数会被解释到基类而不是子类。

2.
下面两个结构体
1
2
3
4
5
6
7
8
9
10
struct One{
     double d;
     char c;
     int i;
}
struct Two{
     char c;
     double d;
     int i;
}
在#pragma pack(4)和#pragma pack(8)的情况下,结构体的大小分别是


3.
1
2
Class A *pclassa= new ClassA[5];
delete pclassa;
则类ClassA的构造函数和析构函数的执行次数分别为(  5,1  )

Class A *pclassa=newClassA[5];  new了五个对象,所以构造5次,然后Pclass指向这五个对象
deletepclassa;  析构一次,delete[]pclassa   这样就析构5次
这边考察delete和delete[] 的区别。
delete和delete[]都能释放指针所指向的内存区域。
但delete只会调用一次析构函数,而delete[]还会调用后续所有对象的析构函数。
当数据类型为基本数据类型时,用delete和delete[]都可以,因为基本数据类型没有析构函数。

4. [例]下列选项哪些是正确的(D)

A.对于unordered_map和map这两个容器,迭代器的有效性皆不受删除操作影响

B.对于unordered_map和map这两个容器,迭代器的有效性皆不受插入操作影响

C.为了保证代码的异常安全性,应该避免在构造函数中抛异常

D.为了保证代码的异常安全性,应该避免在析构函数中抛异常


A选项:当unorder_map和map某迭代器it指向的元素被删除时,只有该迭代器it失效,其他的迭代器不会失效。

B选项:map插入时不会引起迭代器失效;unorder_map插入时一般情况下不会引起迭代器失效,只有当容器增长到需要rehash时,原来的所有迭代器失效。

C选项:构造函数抛出异常后,已经构造的成员对象会被逆序析构,申请的内存资源会被系统释放,不会调用析构函数。而且构造函数抛出异常是唯一表明构造失败的方法。

D选项:effective C++“条款08:别让异常逃离析构函数”指出来如果析构函数抛出异常,对于vector<Widget>这样的一个对象数组,如果第一个Widget析构有异常抛出,这时候还要销毁数组中剩下的Widget否则会造成内存泄漏,但是如果剩下的Widget析构时也抛出异常,就会两个异常同时存在,程序如果不是结束执行就会产生不明确行为。即使不是使用容器或数组,在析构函数中抛出异常也可能导致程序过早结束或不明确行为。


5.

下列程序编译时会出现错误,请根据行号选择错误位置( )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
using namespace std;
class A{
   int a1;
protected :
   int a2;
public :
   int a3;
};
class B:  public A{
   int b1;
protected :
   int b2;
public :
   int b3;
};
class C: private B{
   int c1;
protected :
   int c2;
public :
   int c3;
};
int main(){
   B obb;
   C obc;
   cout<<obb.a1; //1
   cout<<obb.a2; //2
   cout<<obb.a3; //3
   cout<<obc.b1; //4
   cout<<obc.b2; //5
   cout<<obc.b3; //6
   cout<<obc.c3; //7
   return 0;
}
  • 1,2
  • 2,5,7
  • 3,4,7
  • 4,5,6

解析:

类的继承后方法属性变化:
private 属性不能够被继承。
使用private继承,父类的protected和public属性在子类中变为private;
使用protected继承,父类的protected和public属性在子类中变为protected;
使用public继承,父类中的protected和public属性不发生改变; 
private, public, protected 访问标号的访问范围:
private:只能由1.该类中的函数、2.其友元函数访问。
不能被任何其他访问,该类的对象也不能访问。
protected:可以被1.该类中的函数、2.子类的函数、以及3.其友元函数访问。
但不能被该类的对象访问。
public:可以被1.该类中的函数、2.子类的函数、3.其友元函数访问,也可以由4.该类的对象访问。
注:友元函数包括3种:设为友元的普通的非成员函数;设为友元的其他类的成员函数;设为友元类中的所有成员函数。


1
2
3
4
5
cout<<obb.a1; //1   a1是A私有的
cout<<obb.a2; //2   a2是A开放给派生类使用的,客户代码不能使用
cout<<obc.b1; //4   C是私有继承于B,不能使用B的成员
cout<<obc.b2; //5   同上
cout<<obc.b3; //6   同上


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值