- 作者: 雪山肥鱼
- 时间:20210905 12:45
- 目的: 多继承中的虚函数于析构
父类无虚析构的问题
直接抛异常
class Base
{
public:
virtual void f()
{
cout << "Base::f()" << endl;
}
virtual void g()
{
cout << "Base::g()" << endl;
}
virtual void h()
{
cout << "Base::h()" << endl;
}
};
class Base2
{
public:
virtual void hBase2()
{
cout << "Base2::hBase2()" << endl();
}
};
class Derive :public Base, public Base2
{
public:
virtual void i()
{
cout << "Derive::self i()" << endl;
}
virtual void g()
{
cout << "Derive::g()" << endl;
}
void myselffunc() {}
};
int main(int argc, char**argv)
{
Base2 *pb2 = new Derive();
//根据布局得到 编译器可能是这么玩的:
//Derive *temp = new Derive();
//Base2 *pb2 = (Base2*)((char*)temp + sizeof(Base))
delete pb2;//报异常了!
return 0;
}
多继承中的析构
![](https://img.haomeiwen.com/i25953572/c89eaa06ac0a41aa.png)
注:只有多继承时,才会牵扯到this指针的调整
对于这行代码
Base2 *pb2 = new Derive();
//Base2 *pb2 = (Base2*)((char*)temp + sizeof(Base))
- 如果base2里没有析构函数,编译器会直接删除以Base2为首的内存。则注定抛异常。
- 同样对于base2 ,非虚函数的 析构函数来讲,一样的会抛异常。
- 非虚函数的析构,编译器会把他当作普通成员函数,直接执行静态绑定
Base2 中有析构函数, 则调用顺序为
~Derive() -> ~Base2() -> ~Base();
Derive 此时如果没有虚析构函数,则编译器会自动为Derive 合成一个虚析构函数。进行调用。
主要是父类要有虚析构函数
索性统一编程规范:
编程规范:继承关系中一定要有虚析构,不能省略。子类的析构函数,加不加都行,最好都加上virtual标识
父类的析构函数是虚函数,则牵扯到虚函数表。析构时编译器会调整this指针,指向开头的Base,后续详谈。
多重继承的内存布局
![](https://img.haomeiwen.com/i25953572/bd79dcf5fd39b68e.png)
为Base Base2父类增加虚析构函数
多继承中的 thunk
同时发现 子类的 第二张虚函数表,即会多一个 thunk. 这东西专门用在多继承中。
从第二个虚函数表起,有这个东东。主要作用是:调整this指针 因为第一个父类和 子类共用同一张虚函数表,所以不用调整this指针。
thunk作用:
- 调整this 指针到对象开头
- 调用整个子类对象的析构函数(一组析构函数)
-
流程:
反汇编示意.png
![](https://img.haomeiwen.com/i25953572/91daf8948960477a.png)
继承关系中 父类的析构函数必须为虚析构
- 如果不是 只会析构 父类自己的部分,不会析构子类
- 如果是,则虚析构函数在虚函数表中
- 则子类的析构函数,用一组析构函数替换了父类虚函数表中的析构函数。如下(有顺序关系)
- 子类析构函数
- 父类析构函数
- 如果是多继承关系
- 子类析构函数
- 父类2 析构函数
- 父类1 析构函数
网友评论