C++:析构函数
創(chuàng)建對(duì)象時(shí)系統(tǒng)會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù)進(jìn)行初始化工作,同樣,銷(xiāo)毀對(duì)象時(shí)系統(tǒng)也會(huì)自動(dòng)調(diào)用一個(gè)函數(shù)來(lái)進(jìn)行清理工作,例如釋放分配的內(nèi)存、關(guān)閉打開(kāi)的文件等,這個(gè)函數(shù)就是析構(gòu)函數(shù)。
析構(gòu)函數(shù)(destructor) 與構(gòu)造函數(shù)相反,當(dāng)對(duì)象結(jié)束其生命周期,如對(duì)象所在的函數(shù)已調(diào)用完畢時(shí),系統(tǒng)自動(dòng)執(zhí)行析構(gòu)函數(shù)。析構(gòu)函數(shù)往往用來(lái)做“清理善后” 的工作(例如在建立對(duì)象時(shí)用new開(kāi)辟了一片內(nèi)存空間,delete會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)后釋放內(nèi)存)。
作用:“清理善后”工作 ? ? ? ?? 命名方式:與類(lèi)名相同,在前面加位取反符~
與構(gòu)造函數(shù)相反,當(dāng)對(duì)象結(jié)束其生命周期,如對(duì)象所在的函數(shù)已調(diào)用完畢時(shí),系統(tǒng)會(huì)自動(dòng)執(zhí)行析構(gòu)函數(shù)。以C++語(yǔ)言為例:析構(gòu)函數(shù)名也應(yīng)與類(lèi)名相同,只是在函數(shù)名前面加一個(gè)位取反符~,例如~stud( ),以區(qū)別于構(gòu)造函數(shù)。它不能帶任何參數(shù),也沒(méi)有返回值(包括void類(lèi)型),(析構(gòu)函數(shù)(Destructor)也是一種特殊的成員函數(shù),沒(méi)有返回值,不需要程序員顯式調(diào)用(程序員也沒(méi)法顯式調(diào)用),而是在銷(xiāo)毀對(duì)象時(shí)自動(dòng)執(zhí)行。構(gòu)造函數(shù)的名字和類(lèi)名相同,而析構(gòu)函數(shù)的名字是在類(lèi)名前面加一個(gè)~符號(hào))。只能有一個(gè)析構(gòu)函數(shù),不能重載。如果用戶(hù)沒(méi)有編寫(xiě)析構(gòu)函數(shù),編譯系統(tǒng)會(huì)自動(dòng)生成一個(gè)缺省的析構(gòu)函數(shù)(即使自定義了析構(gòu)函數(shù),編譯器也總是會(huì)為我們合成一個(gè)析構(gòu)函數(shù),并且如果自定義了析構(gòu)函數(shù),編譯器在執(zhí)行時(shí)會(huì)先調(diào)用自定義的析構(gòu)函數(shù)再調(diào)用合成的析構(gòu)函數(shù)),它也不進(jìn)行任何操作。所以許多簡(jiǎn)單的類(lèi)中沒(méi)有用顯式的析構(gòu)函數(shù)。
C++中的析構(gòu)函數(shù)格式:
class <類(lèi)名>
{public:~<類(lèi)名>();
};
<類(lèi)名>::~<類(lèi)名>()
{//函數(shù)體
}
以下方式定義也是可以的:
class T
{public:~T();
};T::~T()
{//函數(shù)體
}
PS:注意:析構(gòu)函數(shù)沒(méi)有參數(shù),不能被重載,因此一個(gè)類(lèi)只能有一個(gè)析構(gòu)函數(shù)。如果用戶(hù)沒(méi)有定義,編譯器會(huì)自動(dòng)生成一個(gè)默認(rèn)的析構(gòu)函數(shù)。當(dāng)程序中沒(méi)有析構(gòu)函數(shù)時(shí),系統(tǒng)會(huì)自動(dòng)生成以下析構(gòu)函數(shù):
<類(lèi)名>::~<類(lèi)名>(){},即不執(zhí)行任何操作。
定義了一個(gè) VLA 類(lèi)來(lái)模擬變長(zhǎng)數(shù)組,它使用一個(gè)構(gòu)造函數(shù)為數(shù)組分配內(nèi)存,這些內(nèi)存在數(shù)組被銷(xiāo)毀后不會(huì)自動(dòng)釋放,所以非常有必要再添加一個(gè)析構(gòu)函數(shù),專(zhuān)門(mén)用來(lái)釋放已經(jīng)分配的內(nèi)存。請(qǐng)看下面的完整示例:
#include <iostream>
using namespace std;class VLA{
public:VLA(int len); //構(gòu)造函數(shù)~VLA(); //析構(gòu)函數(shù)
public:void input(); //從控制臺(tái)輸入數(shù)組元素void show(); //顯示數(shù)組元素
private:int *at(int i); //獲取第i個(gè)元素的指針
private:const int m_len; //數(shù)組長(zhǎng)度int *m_arr; //數(shù)組指針int *m_p; //指向數(shù)組第i個(gè)元素的指針
};VLA::VLA(int len): m_len(len){ //使用初始化列表來(lái)給 m_len 賦值if(len > 0){ m_arr = new int[len]; /*分配內(nèi)存*/ }else{ m_arr = NULL; }
}
VLA::~VLA(){delete[] m_arr; //釋放內(nèi)存
}
void VLA::input(){for(int i=0; m_p=at(i); i++){ cin>>*at(i); }
}
void VLA::show(){for(int i=0; m_p=at(i); i++){if(i == m_len - 1){ cout<<*at(i)<<endl; }else{ cout<<*at(i)<<", "; }}
}
int * VLA::at(int i){if(!m_arr || i<0 || i>=m_len){ return NULL; }else{ return m_arr + i; }
}int main(){//創(chuàng)建一個(gè)有n個(gè)元素的數(shù)組(對(duì)象)int n;cout<<"Input array length: ";cin>>n;VLA *parr = new VLA(n);//輸入數(shù)組元素cout<<"Input "<<n<<" numbers: ";parr -> input();//輸出數(shù)組元素cout<<"Elements: ";parr -> show();//刪除數(shù)組(對(duì)象)delete parr;return 0;
}
運(yùn)行結(jié)果:
Input array length: 5
Input 5 numbers: 99 23 45 10 100
Elements: 99, 23, 45, 10, 100
~VLA()就是 VLA 類(lèi)的析構(gòu)函數(shù),它的唯一作用就是在刪除對(duì)象(第 53 行代碼)后釋放已經(jīng)分配的內(nèi)存。
函數(shù)名是標(biāo)識(shí)符的一種,原則上標(biāo)識(shí)符的命名中不允許出現(xiàn)~符號(hào),在析構(gòu)函數(shù)的名字中出現(xiàn)的~可以認(rèn)為是一種特殊情況,目的是為了和構(gòu)造函數(shù)的名字加以對(duì)比和區(qū)分。
注意:at() 函數(shù)只在類(lèi)的內(nèi)部使用,所以將它聲明為 private 屬性;m_len 變量不允許修改,所以用 const 進(jìn)行了限制,這樣就只能使用初始化列表來(lái)進(jìn)行賦值。
C++ 中的 new 和 delete 分別用來(lái)分配和釋放內(nèi)存,它們與C語(yǔ)言中 malloc()、free() 最大的一個(gè)不同之處在于:用 new 分配內(nèi)存時(shí)會(huì)調(diào)用構(gòu)造函數(shù),用 delete 釋放內(nèi)存時(shí)會(huì)調(diào)用析構(gòu)函數(shù)。構(gòu)造函數(shù)和析構(gòu)函數(shù)對(duì)于類(lèi)來(lái)說(shuō)是不可或缺的,所以在C++中我們非常鼓勵(lì)使用 new 和 delete。
--------析構(gòu)函數(shù)的執(zhí)行時(shí)機(jī):
析構(gòu)函數(shù)在對(duì)象被銷(xiāo)毀時(shí)調(diào)用,而對(duì)象的銷(xiāo)毀時(shí)機(jī)與它所在的內(nèi)存區(qū)域有關(guān)。
在所有函數(shù)之外創(chuàng)建的對(duì)象是全局對(duì)象,它和全局變量類(lèi)似,位于內(nèi)存分區(qū)中的全局?jǐn)?shù)據(jù)區(qū),程序在結(jié)束執(zhí)行時(shí)會(huì)調(diào)用這些對(duì)象的析構(gòu)函數(shù)。
在函數(shù)內(nèi)部創(chuàng)建的對(duì)象是局部對(duì)象,它和局部變量類(lèi)似,位于棧區(qū),函數(shù)執(zhí)行結(jié)束時(shí)會(huì)調(diào)用這些對(duì)象的析構(gòu)函數(shù)。
new 創(chuàng)建的對(duì)象位于堆區(qū),通過(guò) delete 刪除時(shí)才會(huì)調(diào)用析構(gòu)函數(shù);如果沒(méi)有 delete,析構(gòu)函數(shù)就不會(huì)被執(zhí)行。
下面的例子演示了析構(gòu)函數(shù)的執(zhí)行:
#include <iostream>
#include <string>
using namespace std;class Demo{
public:Demo(string s);~Demo();
private:string m_s;
};
Demo::Demo(string s): m_s(s){ }
Demo::~Demo(){ cout<<m_s<<endl; }void func(){//局部對(duì)象Demo obj1("1");
}//全局對(duì)象
Demo obj2("2");int main(){//局部對(duì)象Demo obj3("3");//new創(chuàng)建的對(duì)象Demo *pobj4 = new Demo("4");func();cout<<"main"<<endl;return 0;
}
運(yùn)行結(jié)果:
1
main
3
2
?
?
總結(jié)