前面我们分析了 继承多个类的情况。上一次分析的这样的情况:
今天看虚基类。先复习一下虚基类:类似下面这样的图
复习虚基类可以解决的问题:
在这之前先要复习一下多继承同一个爷爷类时带来的问题
空间问题
效率问题
二义性问题
//虚基类问题分析
class Teacher14Grand {
public:
int grandage;
};
class Teacher14Father1 :public Teacher14Grand{
};
class Teacher14Father2 :public Teacher14Grand {
};
class Teacher14 :public Teacher14Father1, public Teacher14Father2 {
};
void main() {
cout << sizeof(Teacher14Grand) << endl; //4
cout << sizeof(Teacher14Father1) << endl; //4
cout << sizeof(Teacher14Father2) << endl; //4
cout << sizeof(Teacher14) << endl; //8
//从上述结果可以看到,Teacher14的大小是8。这个大小是8,应该是从Teacher14Fathrer1继承了4个字节,从Teacher14Fathrer2继承了4个字节
//问题:二义性问题,因为Teacher14有的内存布局中是 继承了Teacher14Father1的4个字节,继承了Teacher14Father2的4个字节
//且Teacher14Father1的4个字节是从Teacher14Grand继承的 int grandage
//且Teacher14Father2的4个字节是从Teacher14Grand继承的 int grandage
//因此当我们使用Teacher14的对象 tea给grandage赋值的时候,编译器不知道是给Teacher14Father1中的grandage赋值,还是给Teacher14Father2中的grandage赋值,这个就是二义性问题的原因
Teacher14 tea;
//tea.grandage = 80;//像这样写就是有问题的,编译器不知道给那个grandage赋值。
//fix方案,指定给那个具体的赋值
tea.Teacher14Father1::grandage = 90;
//当然,访问的时候,也要指定的访问那一个grandage
cout << tea.Teacher14Father1::grandage << endl;
//最开始猜想:由于tea.Teacher14Father2::grandage没有赋过值,因此这个值是乱码
//但是实际上写这一行的时候,会有build error。提示“使用了未初始化的局部变量tea”
//cout << tea.Teacher14Father2::grandage << endl;
二义性问题引申:
//写到这里的时候,进而会想到一个问题。如果子类有和父类同名的变量,在这种case下,会不会也覆盖呢?
class Teacher15Grand {
public:
int grandage = 80;
};
class Teacher15Father1 :public Teacher15Grand {
public:
int grandage;
};
class Teacher15Father2 :public Teacher15Grand {
};
class Teacher15 :public Teacher15Father1, public Teacher15Father2 {
};
//写到这里的时候,进而会想到一个问题。如果子类有和父类同名的变量,在这种case下,会不会也覆盖呢?
//我们在Teacher15中验证一下。注意:这里不同的是在 Teacher15Grand 中有给grandage初始值为88
cout << sizeof(Teacher15Grand) << endl; //4
cout << sizeof(Teacher15Father1) << endl;//8,说明,还是会继承Teacher15Grand中的grandage,然后还有自己的grandage,一共占用8个字节
cout << sizeof(Teacher15Father2) << endl;//4
cout << sizeof(Teacher15) << endl;//12
//那么又有一个问题了,怎么去访问这3个有效的成员呢?
Teacher15 tea15;
tea15.Teacher15Father1::grandage = 99; //对于 Teacher15Father1的grandage进行赋值
tea15.Teacher15Father1::Teacher15Grand::grandage = 199;//对于
tea15.Teacher15Father2::grandage = 299;
cout << tea15.Teacher15Father1::grandage << endl;
cout << tea15.Teacher15Father1::Teacher15Grand::grandage << endl;
cout << tea15.Teacher15Father2::grandage << endl;
debug代码得到内存图
因此我们可以画出来Teacher14的 内存图
复习虚基类可以解决的问题:
继承爷爷的类使用 virtual 继承即可
class Teacher16Grand {
public:
int grandage;
};
//让中间类虚继承爷爷
class Teacher16Father1 :public virtual Teacher16Grand {
};
//让中间类虚继承爷爷
class Teacher16Father2 :public virtual Teacher16Grand {
};
class Teacher16 :public Teacher16Father1, public Teacher16Father2 {
};
void main() {
cout << sizeof(Teacher16Grand) << endl; //4
cout << sizeof(Teacher16Father1) << endl; //8
cout << sizeof(Teacher16Father2) << endl; //8
cout << sizeof(Teacher16) << endl; //12
//从上面的看到,Teacher16Fa;ther1 和 Teacher16Father2都多了4个字节。
//这个四个字节是什么呢? 是虚基类表指针
//引入两个概念 虚基类表,虚基类表指针
// 只要有虚基类,就会有虚基类表 vbtable-- - virtual base table
// 如果有子类的对象生成,就会有虚基类表指针 vbptr virtual base table pointer
Teacher16 tea;
tea.grandage = 8;
cout << "断点在这里" << endl;
}
使用2017开发人员命令试一下:
引出虚基类表,虚基类表指针 概念
只要有虚基类,就会有虚基类表 vbtable --- virtual base table
如果有子类的对象生成,就会有虚基类表指针 vbptr virtual base table pointer
注意和 虚函数表 和 虚函数指针区分 vtable (virtual table) , vptr 也叫做vfptr (virtual table pointer)
有虚基类和虚函数的同时存在时候 的内存图
内存图中先存储虚基类指针,然后再存储虚函数指针
class Teacher17Grand {
public:
int grandage;
public:
void virtual grandfunc() {
}
};
//让中间类虚继承爷爷
class Teacher17Father1 :public virtual Teacher17Grand {
};
//让中间类虚继承爷爷
class Teacher17Father2 :public virtual Teacher17Grand {
};
class Teacher17 :public Teacher17Father1, public Teacher17Father2 {
};
void main() {
//那么当既有虚基类表,也有虚函数表的时候,虚基类指针 和虚函数表指针那个放在前面呢?
cout << sizeof(Teacher17Grand) << endl; //8
cout << sizeof(Teacher17Father1) << endl; //12
cout << sizeof(Teacher17Father2) << endl; //12
cout << sizeof(Teacher17) << endl; //16
Teacher17 tea;
tea.grandage = 8;
cout << "断点在这里" << endl;
}
当虚基类,虚函数,爷爷类,父亲类,子类都有成员变量的时候
class Teacher17Grand {
public:
int grandage;
public:
void virtual grandfunc() {
}
};
//让中间类虚继承爷爷
class Teacher17Father1 :public virtual Teacher17Grand {
public:
int father1age;
};
//让中间类虚继承爷爷
class Teacher17Father2 :public virtual Teacher17Grand {
public:
int father2age;
};
class Teacher17 :public Teacher17Father1, public Teacher17Father2 {
public:
int teaage;
};
void main() {
//我们接着看,这些成员变量的值都是在哪里存储着?
cout << sizeof(Teacher17Grand) << endl; // 8 一个vptr+ 一个int
cout << sizeof(Teacher17Father1) << endl; // 16 一个vbptr + 一个自己int + 一个vptr + 一个父类 int
cout << sizeof(Teacher17Father2) << endl; // 16 一个vbptr + 一个自己int + 一个vptr + 一个父类 int
cout << sizeof(Teacher17) << endl; // 28 father1vbptr + father1age + father2vbptr + father2age +自己int + 爷爷vptr + 爷爷age
Teacher17 tea;
tea.father1age = 1;
tea.father2age = 2;
tea.teaage = 3;
tea.grandage = 4;
cout << "断点在这里" << endl;
}
整理