1、对象生命周期结束,被销毁时
2、delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类析构函数是虚函数时;
3、对象A是对象B的成员,B的析构函数被调用时,对象A的析构函数也被调用。
举例来说:
class A
{
public:
A() {ma = new int(10);}//申请一个int大小的空间,初值为10
A(int sz) {ma = new int(sz);}
virtual ~A() { delete ma;}
int* ma;
};
class B:public A
{
public:
B() {mb = new[10] int();}//申请了10个int大小空间,(默认)初值均为0
B(int sz) {mb = new int(sz);}
virtual ~B() {delete mb;}
int* mb;
};
void DoSome()
{
A a;
}
void mainForm()
{
A a; //语句一
DoSome(); //语句二
A* a2 = new A(); //语句三
//doOther ...
A* my = new B(); //语句四
}
执行语句一,结果是mainForm函数运行结束的时候,也就是a的生命周期结束才调用~A()
执行语句二,DoSome()函数体运行完成后,DoSome中a栈空间被释放前会调用~A()
执行语句三,则要等整个程序运行结束,注意不是主程序,而是整个程序。而且mainForm执行完,会造成a2指向的内存泄露,a2变成野指针(怎么解决这个问题呢? 第一种方法可以语句三后面不再使用a2之后,主动delete a2,第二种方法可以直接调用析构函数a2->~A(),第一种方法delete a2的时候会主动调用析构)
执行语句四,这个例子和语句三差不多,这里只是为了说明正确调用析构函数释放堆空间,需要结合虚函数的使用
(注:理解生命周期结束的含义,比如栈对象的生命周期是从变量定义到该函数体结束,而堆对象和静态对象的生命周期是是从定义到整个程序运行结束)
这里说一个容易出错的例子,比如有一个类叫做Base,有一个成员类型是自身的静态指针,如下:
class Base
{
public:
Base()
{
cout << "Base() construction" << endl;
}
virtual ~Base();
Base * GetInstance()
{
if (mb == nullptr)
{
mb = new Base();
}
return mb;
}
int iData;
static Base *mb;
};
Base* Base::mb = nullptr;
Base::~Base()
{
cout << "~Base() called" << endl;
delete mb;//这里会出问题,因为delete的时候会调用自身的析构函数,析构函数里面要执行delete,而mb是静态成员,delete并不会释放掉mb开辟的空间,而是一直调用Base的析构,下一行mb=nullptr也不会执行,这样就会形成一个死循环导致栈溢出(函数递归)
mb = nullptr;
}
class A :public Base
{
public:
A()
{
cout << "A() construction" << endl;
}
~A()
{
cout << "~A() called" << endl;
}
};
void Do()
{
Base *b = new A();
b->GetInstance();
b->~Base();
}
void main()
{
Do();
system("pause");
}
上述代码的结果就是不断重复调用base的析构函数:~Base(),直到栈溢出。
如果把静态改为非静态,运行结果如下:
两者的区别就在mb的生命周期不同,如果是类非静态成员,执行delete则会释放申请的内存空间,然后结束。