C++继承机制下析构和构造函数的执行次数与分析

析构函数在下边3种情况时被调用:

  1. 对象生命周期结束,被销毁时;
  2. delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
  3. 对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。

派生体系中:

析构函数的调用规则:

  1. 对于派生类对象,若不使用基类对象指向他,直接delete该则调用它的析构函数,再调用它成员、基类的析构函数,依此类推。
  2. 自动对象(局部的)在离开作用域的时候自动调用析构函数。(确切的说是编译器在离开对象作用域的时候添加析构函数的代码)
  3. new出来的对象要有delete时才调用析构函数。
  4. 基类指针可以指向派生类,这时候必须吧基类的析构函数声明为虚函数,否则无法释放子类资源,会导致内存泄漏。

构造与析构顺序相反:

  1. 当派生类中不含对象成员时 :
    在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
    析构函数相反。
  2. 当派生类中含有对象成员时 :
    在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
    析构函数相反。

看些例子:

#include <iostream>
using namespace std;
class A { public: ~A(); };//情况1
//class A { public: virtual ~A(); };//情况2
 A::~A() { printf("delete A "); }
class B :public A { public: ~B(); };
 B::~B() { printf("delete B "); }
int main() {
    //int a;
    //cin >> a;
    A *pa = new B();
    delete pa;
    //cout << "Hello World!" << endl;
}

看A加不加virtual时候的输出

结果:

情况1:
在这里插入图片描述

情况2:

在这里插入图片描述

解析:

情况1中,父类A析构函数不是virtual,故无法使用虚函数机制调用子类B的析构函数。

情况2中,父类A析构函数是virtual,使用虚函数机制调用子类B的析构函数,由于B类中含有A对象,根据上述析构函数调用第三条规则,故会在之后调用A的析构函数。

-----------------------分界线-------------------------------------------------------------------------------

需要指出的是,C++将内存处理交给用户,所以在上述情况中,虽然我们内存释放的不正确,例如B中有堆上的数据,这会导致这些堆上的数据不能释放,但是,IDE仍然认为这里我们new出的这个B类型对象被释放了,所以,这里就产生了常见的内存泄漏:

#include <iostream>
using namespace std;
class A { 
    public: ~A(); //情况1
    //public: virtual ~A();//情况2
    int x = 1;
    virtual void printX() {

    }
};
A::~A() { printf("delete A "); }
class B :public A {
public:
    int *leakMemory  = new int[10];//泄漏
    virtual void printX() {
        //cout << x << endl;
        cout << 2 << endl;
    }
    ~B(); 
};
B::~B() { printf("delete B "); }
int main() {
    B* pb= new B();
    A* pa= pb;
    A* paa = pa;
    paa->printX();
    delete pa;
    pb->printX();
    //cout << "Hello World!" << endl;
}

这里leakmemory的释放由~ B来做,但是在没有将~ A设置为virtual的情况下,虽然~B没有执行,leakmemory没有被释放,但是IDE仍然认为这个B对象已经被释放,我们无法再使用这个对象,这就造成了内存泄漏。
在这里插入图片描述

稍微复杂点的的例子,可以判断结果检验自己是否理解:

#include <iostream>
using namespace std;
class A {
public:
    A() { cout << "A" << endl; }
    virtual void AB_test() { cout << "A_test" << endl; }
    //~A() { cout << "~A" << endl; AB_test(); }//case 3
    virtual ~A() { cout << "~A" << endl; AB_test(); }//case 4
};

class B :public A {
public:
    B() { cout << "B" << endl; }
    ~B() { cout << "~B" << endl; AB_test(); }
    virtual void AB_test() { cout << "B_test" << endl; }
};
int main()
{
    A* b = new B;
    delete b;
    return 0;
}

结果:

case3:

在这里插入图片描述

case4:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值