幾乎在大部分時(shí)候,我們是不需要顯式的調(diào)用析構(gòu)函數(shù)的。顯式的調(diào)用析構(gòu)函數(shù)是一件非常危險(xiǎn)的事情,因?yàn)槿绻到y(tǒng)會(huì)調(diào)用析構(gòu)函數(shù),無論我們自己是否已經(jīng)調(diào)用過,仍然會(huì)再次調(diào)用。換句話說,我們自己所謂的顯式調(diào)用析構(gòu)函數(shù),實(shí)際上只是調(diào)用了一個(gè)成員函數(shù),并沒有真正意義上的讓對(duì)象“析構(gòu)”。
為了理解這個(gè)問題,我們必須首先弄明白“堆”和“棧”的概念。
堆區(qū)(heap) —— 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表。
棧區(qū)(stack)—— 由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
我們構(gòu)造對(duì)象,往往都是在一段語句體中,比如函數(shù),判斷,循環(huán),還有就直接被一對(duì)“{}”包含的語句體。這個(gè)對(duì)象在語句體中被創(chuàng)建,在語句體結(jié)束的時(shí)候被銷毀。問題就在于,這樣的對(duì)象在生命周期中是存在于棧上的。也就是說,如何管理,是系統(tǒng)完成而程序員不能控制的。所以,即使我們調(diào)用了析構(gòu),在對(duì)象生命周期結(jié)束后,系統(tǒng)仍然會(huì)再調(diào)用一次析構(gòu)函數(shù),將其在棧上銷毀,實(shí)現(xiàn)真正的析構(gòu)。
所以,如果我們?cè)谖鰳?gòu)函數(shù)中有清除堆數(shù)據(jù)的語句,調(diào)用兩次意味著第二次會(huì)試圖清理已經(jīng)被清理過了的,根本不再存在的數(shù)據(jù)!這是件會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤的問題,并且在編譯的時(shí)候不會(huì)告訴你!
在網(wǎng)上找到這幾句話,說得很好?。?br>//顯式調(diào)用的時(shí)候,析構(gòu)函數(shù)相當(dāng)于的一個(gè)普通的成員函數(shù)
//編譯器隱式調(diào)用析構(gòu)函數(shù),如分配了對(duì)內(nèi)存,顯式調(diào)用析構(gòu)的話引起重復(fù)釋放堆內(nèi)存的異常
//把一個(gè)對(duì)象看作占用了部分棧內(nèi)存,占用了部分堆內(nèi)存(如果申請(qǐng)了的話),這樣便于理解這個(gè)問題
//系統(tǒng)隱式調(diào)用析構(gòu)函數(shù)的時(shí)候,會(huì)加入釋放棧內(nèi)存的動(dòng)作(而堆內(nèi)存則由用戶手工的釋放)
//用戶顯式調(diào)用析構(gòu)函數(shù)的時(shí)候,只是單純執(zhí)行析構(gòu)函數(shù)內(nèi)的語句,不會(huì)釋放棧內(nèi)存,摧毀對(duì)象
系統(tǒng)在什么情況下不會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)呢?顯然,如果對(duì)象被建立在堆上,系統(tǒng)就不會(huì)自動(dòng)調(diào)用。一個(gè)常見的例子是new...delete組合。但是好在調(diào)用delete的時(shí)候,析構(gòu)函數(shù)還是被自動(dòng)調(diào)用了。很罕見的例外在于使用布局new的時(shí)候,在delete設(shè)置的緩存之前,需要顯式調(diào)用的析構(gòu)函數(shù),這實(shí)在是很少見的情況。
所以,結(jié)論是,一般不要自作聰明的去調(diào)用析構(gòu)函數(shù)?;蛘咭悄悴幌勇闊┑脑挘鰳?gòu)之前最好先看看堆上的數(shù)據(jù)是不是已經(jīng)被釋放過了。放出一個(gè)演示的例子,大家可以參考一下哈。
#ifndef A_HPP
#define A_HPP
#include <iostream>
using namespace std;
class A
{
private:
int a;
int* temp;
bool heap_deleted;
public:
A(int _a);
A(const A& _a);
~A();
void change(int x);
void show() const;
};
#endif
#include "a.hpp"
A::A(int _a): heap_deleted(false)
{
temp = new int;
*temp = _a;
a = *temp;
cout<< "A Constructor!" << endl;
}
A::A(const A& _a): heap_deleted(false)
{
temp = new int;
*temp = _a.a;
a = *temp;
cout << "A Copy Constructor" << endl;
}
A::~A()
{
if ( heap_deleted == false){
cout << "temp at: " << temp << endl;
delete temp;
heap_deleted = true;
cout << "Heap Deleted!\n";
}
else {
cout << "Heap already Deleted!\n";
}
cout << "A Destroyed!" << endl;
}
void A::change(int x)
{
a = x;
}
void A::show() const
{
cout << "a = " << a << endl;
}
#include "a.hpp"
int main(int argc, char* argv[])
{
A a(1);
a.~A();
a.show();
cout << "main() end\n";
a.change(2);
a.show();
return 0;
}