继承与虚函数练习

Tip1 

 基类私有成员变量在子类中都不能直接访问,不是因为没有被子类继承,而是权限问题

Tip2 

满足多态的父子对象,父类对象和子类对象前4个字节都是虚表指针(vs2019下),父类与子类指向的是各自的虚表。

 Tip3

  1. 子类构造函数会调用父类构造函数初始化父类成员,再初始化子类成员。
  2. 子类析构函数会自动调用父类的析构函数析构父类部分成员,析构顺序和构造顺序相反。
  3. 先构造父类,再构造子类,先析构子类,再析构父类
  4. 子类构造函数的定义有时需要参考基类的构造函数:当创建子类对象时,如果父类的构造函数需要参数的话,那必须通过子类的构造函数来传参。

Tip4 

 子类在继承父类时,可以不明确指定继承方式,默认为private

test1:虚表的个数

假设D类先继承B1,然后继承B2,B1和B2基类均包含虚函数,D类对B1和B2基类的虚函数重写了,并且D类增加了新的虚函数,则:

A.D类对象模型中包含了3个虚表指针

B.D类对象有两个虚表,D类新增加的虚函数放在第一张虚表最后

C.D类对象有两个虚表,D类新增加的虚函数放在第二张虚表最后

D.以上全部错误

解析:

A.D类有几个父类,如果父类有虚函数,则就会有几张虚表,自身子类不会产生多余的虚表,所以只有2张虚表

B.正确

C.子类自己的虚函数只会放到第一个父类的虚表后面,其他父类的虚表不需要存储,因为存储了也不能调用

D.错误

 总结:

子类继承几个父类就有几张虚表,自己不会产生多余的虚表

子类自己的虚函数只会放到第一个父类的虚表后面。

 test2 :多继承的内存分布

下面哪项结果是正确的( )
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 
{ public: int _d; };

int main()
{
    Derive d;
    Base1* p1 = &d;
    Base2* p2 = &d;
    Derive* p3 = &d;
    return 0;
}

A.p1 == p2 == p3

B.p1 < p2 < p3

C.p1 == p3 != p2

D.p1 != p2 != p3

分析:

p1和p2虽然都是其父类,但在子类内存模型中,其位置不同,所以p1和p2所指子类的位置也不相同,因此p1!=p2,

由于p1对象是第一个被继承的父类类型,所以其地址与子类对象的地址p3所指位置都为子类对象的起始位置,因此p1==p3,所以C正确

 继承与虚函数练习_第1张图片

 test3:菱形继承

关于以下菱形继承说法不正确的是( )
class B {public: int b;};
class C1: public B {public: int c1;};
class C2: public B {public: int c2;};
class D : public C1, public C2 {public: int d;};

A.D总共占了20个字节

B.B中的内容总共在D对象中存储了两份

C.D对象可以直接访问从基类继承的b成员

D.菱形继承存在二义性问题,尽量避免设计菱形继承

分析:

A. C1中b和c1共8个字节,C2中c2和b共8个字节,D自身成员d 4个字节,一共20字节

B.由于菱形继承,最终的父类B在D中会有两份

C.子类对象不能直接访问最顶层基类B中继承下来的b成员,因为在D对象中,b有两份,一份是从C1中继承的,一份是从C2中继承的,直接通过D的对象访问b会存在二义性问题,在访问时候,可以加类名::b,来告诉编译器想要访问C1还是C2中继承下来的b。

D.对的,但如果真有需要,一般采用菱形虚拟继承减少数据冗余

所以选择C.

你可能感兴趣的:(c++,c++,开发语言,笔记)