創建對象時系統會自動調用構造函數進行初始化工作,同樣,銷毀對象時系統也會自動調用一個函數來進行清理工作,例如釋放分配的內存、關閉打開的文件等,這個函數就是析構函數。
析構函數(Destructor)也是一種特殊的成員函數,沒有返回值,不需要程序員顯式調用(程序員也沒法顯式調用),而是在銷毀對象時自動執行。構造函數的名字和類名相同,而析構函數的名字是在類名前面加一個
~
符號。
注意:析構函數沒有參數,不能被重載,因此一個類只能有一個析構函數。如果用戶沒有定義,編譯器會自動生成一個默認的析構函數。
上節我們定義了一個 VLA 類來模擬變長數組,它使用一個構造函數為數組分配內存,這些內存在數組被銷毀後不會自動釋放,所以非常有必要再添加一個析構函數,專門用來釋放已經分配的內存。請看下面的完整示例:
#include <iostream>
using namespace std;
class VLA{
public:
VLA(int len); //構造函數
~VLA(); //析構函數
public:
void input(); //從控制台輸入數組元素
void show(); //顯示數組元素
private:
int *at(int i); //獲取第i個元素的指針
private:
const int m_len; //數組長度
int *m_arr; //數組指針
int *m_p; //指向數組第i個元素的指針
};
VLA::VLA(int len): m_len(len){
if(len > 0){ m_arr = new int[len]; /*分配內存*/ }
else{ m_arr = NULL; }
}
VLA::~VLA(){
delete[] m_arr; //釋放內存
}
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(){
//創建一個有n個元素的數組(對象)
int n;
cout<<"Input array length: ";
cin>>n;
VLA *parr = new VLA(n);
//輸入數組元素
cout<<"Input "<<n<<" numbers: ";
parr -> input();
//輸出數組元素
cout<<"Elements: ";
parr -> show();
//刪除數組(對象)
delete parr;
return 0;
}
運行結果:
Input array length: 5
Input 5 numbers: 99 23 45 10 100
Elements: 99, 23, 45, 10, 100
~VLA()
就是 VLA 類的析構函數,它的唯一作用就是在刪除對象(第 53 行代碼)後釋放已經分配的內存。
函數名是標識符的一種,原則上標識符的命名中不允許出現
~
符號,在析構函數的名字中出現的
~
可以認為是一種特殊情況,目的是為了和構造函數的名字加以對比和區分。
注意:at() 函數只在類的內部使用,所以將它聲明為 private 屬性;m_len 變量不允許修改,所以用 const 限制。
C++ 中的 new 和 delete 分別用來分配和釋放內存,它們與C語言中 malloc()、free() 最大的一個不同之處在於:用 new 分配內存時會調用構造函數,用 delete 釋放內存時會調用析構函數。構造函數和析構函數對於類來說是不可或缺的,所以在C++中我們非常鼓勵使用 new 和 delete。
析構函數的執行時機
析構函數在對象被銷毀時調用,而對象的銷毀時機與它所在的內存區域有關。不了解內存分區的讀者請閱讀《C語言和內存》專題。
在所有函數之外創建的對象是全局對象,它和全局變量類似,位於內存分區中的全局數據區,程序在結束執行時會調用這些對象的析構函數。
在函數內部創建的對象是局部對象,它和局部變量類似,位於棧區,函數執行結束時會調用這些對象的析構函數。
new 創建的對象位於堆區,通過 delete 刪除時才會調用析構函數;如果沒有 delete,析構函數就不會被執行。
下面的例子演示了析構函數的執行。
#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(){
//局部對象
Demo obj1("1");
}
//全局對象
Demo obj2("2");
int main(){
//局部對象
Demo obj3("3");
//new創建的對象
Demo *pobj4 = new Demo("4");
func();
cout<<"main"<<endl;
return 0;
}
運行結果:
1
main
3
2