多重继承:一个派生类有多个基类
虚基类
class A {
public:
protected:
int ma;
};
class B :virtual public A {//虚继承,类A称为虚基类
public:
private:
int mb;
};
通过命令提示输入cpp文件路径/cl cpp文件名.cpp/dlreportSingleClassB 来查看当前类B内存状况
虚基类数据保存在了派生类的最末尾,类的起始部分生成一个vbptr,指向vbtable。vbtable第一行是vbptr向类内存顶部的偏移量为0,第二行是类起始到虚基类数据的偏移量为8个字节
一个派生类对应一个虚基类表,编译时期生成,运行时期放到.rodata只读数据段
当需要访问派生类中虚基类的数据时,在原本普通基类数据位置找到vbptr,再通过偏移量找到虚基类数据
虚基类和虚函数同时存在的情况
class A {
public:
virtual void func() {
cout << "call A::func()" << endl;
}
protected:
int ma;
};
class B :virtual public A {//虚继承,类A称为虚基类
public:
void func() {
cout << "call B::func()" << endl;
}
private:
int mb;
};
int main() {
A* pa = new B();
pa->func();
delete pa;//调用没问题,delete会出错
return 0;
}
基类指针指向派生类对象,永远指向派生类中基类数据的起始地址
在基类有虚函数并且同时使用虚继承时,派生类内存中基类部分数据移到末尾,vfptr在基类数据的头部,也跟着移动了,再在派生类内存顶部生成vbptr
所以pa指向的是vfptr所在的内存(vfptr属于基类数据是因为虚函数在基类中,如果虚函数只属于派生类那么vfptr不会跟着移动)
因为new的内存是从最顶部开辟的,但是delete的地址变为了中间vfptr的地方,上面的内存不能释放
Linux不会出现该问题,会自动偏移到开辟的堆内存顶部
菱形继承:
A
B C
D
A可以称作D的间接的基类
#include<iostream>
using namespace std;
class A {
public:
A(int data) :ma(data) {
cout << "call A()" << endl;
}
~A() {
cout << "call ~A()" << endl;
}
protected:
int ma;
};
class B :public A {
public:
B(int data) :A(data), mb(data) {
cout << "call B()" << endl;
}
~B() {
cout << "call ~B()" << endl;
}
protected:
int mb;
};
class C :public A {
public:
C(int data) :A(data), mc(data) {
cout << "call C()" << endl;
}
~C() {
cout << "call ~C()" << endl;
}
protected:
int mc;
};
class D :public B, public C {
public:
D(int data) :B(data), C(data), md(data) {
cout << "call D()" << endl;
}
~D() {
cout << "call ~D()" << endl;
}
protected:
int md;
};
int main() {
D d(11);
return 0;
}
/*
call A()
call B()
call A()
call C()
call D()
call ~D()
call ~C()
call ~A()
call ~B()
call ~A()
*/
此时会构造、析构两次相同的A数据,需要杜绝这种情况
class B :virtual public A {
public:
B(int data) :A(data), mb(data) {
cout << "call B()" << endl;
}
~B() {
cout << "call ~B()" << endl;
}
protected:
int mb;
};
class C :virtual public A {
public:
C(int data) :A(data), mc(data) {
cout << "call C()" << endl;
}
~C() {
cout << "call ~C()" << endl;
}
protected:
int mc;
};
将所有直接继承A的继承写为虚继承
class D :public B, public C {
public:
D(int data) :B(data), C(data), md(data) {
cout << "call D()" << endl;
}
~D() {
cout << "call ~D()" << endl;
}
protected:
int md;
};
int main() {
D d(11);
return 0;
}
再运行会报错:A没有合适的默认构造函数可用,因为类A的数据移到了D内存的末尾,原先B、C的作用域中只留下了vbtable,A的数据属于了D的作用域,并且只会有一份(所以需要把所有直接继承A的操作写为虚继承,不然还是会有多份A数据)
所以需要D中亲自构造A
class D :public B, public C {
public:
D(int data) :A(data), B(data), C(data), md(data) {
cout << "call D()" << endl;
}
~D() {
cout << "call ~D()" << endl;
}
protected:
int md;
};
/*
call A()
call B()
call C()
call D()
call ~D()
call ~C()
call ~B()
call ~A()
*/