析构函数在下边3种情况时被调用:
- 对象生命周期结束,被销毁时;
- delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
- 对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。
派生体系中:
析构函数的调用规则:
- 对于派生类对象,若不使用基类对象指向他,直接delete该则调用它的析构函数,再调用它成员、基类的析构函数,依此类推。
- 自动对象(局部的)在离开作用域的时候自动调用析构函数。(确切的说是编译器在离开对象作用域的时候添加析构函数的代码)
- new出来的对象要有delete时才调用析构函数。
- 基类指针可以指向派生类,这时候必须吧基类的析构函数声明为虚函数,否则无法释放子类资源,会导致内存泄漏。
构造与析构顺序相反:
- 当派生类中不含对象成员时 :
在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
析构函数相反。 - 当派生类中含有对象成员时 :
在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
析构函数相反。
看些例子:
#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: