建構函數負責「出生時初始化」,析構函數負責「死亡時清理」。它是特殊成員函數:
~ClassName(~ 為位元補數運算子,此處為語法標記)。class VLA { // Variable Length Array
public:
VLA(int len); // 建構:配置記憶體
~VLA(); // 析構:釋放記憶體
void input();
void show();
private:
int* at(int i);
const int m_len;
int* m_arr;
int* m_p;
};
VLA::VLA(int len) : m_len(len) {
m_arr = (len > 0) ? new int[len] : nullptr;
}
VLA::~VLA() {
delete[] m_arr; // 關鍵:配置用 new[],釋放必用 delete[]
}
int main() {
int n; cin >> n;
VLA* arr = new VLA(n); // 堆上建立物件
arr->input();
arr->show();
delete arr; // 觸發 ~VLA(),釋放 m_arr
return 0;
}
鐵律:new 配對 delete,new[] 配對 delete[]。析構函數確保「有配置必釋放」,避免記憶體洩漏。
| 物件類型 | 儲存區 | 析構觸發時機 |
|---|---|---|
| 全域/靜態物件 | 全域資料區 | 程式結束(main 返回後) |
| 區域物件 | 堆疊 | 離開作用域(函數返回、區塊結束) |
動態物件 (new) | 堆積 | 呼叫 delete 時;未 delete 則不會析構 |
#include <iostream>
#include <string>
using namespace std;
class Demo {
public:
Demo(string s) : m_s(s) {}
~Demo() { cout << m_s << endl; }
private:
string m_s;
};
Demo g_obj("2"); // 全域物件
void func() {
Demo local("1"); // 區域物件
} // local 析構,輸出 1
int main() {
Demo main_obj("3"); // 區域物件
Demo* p = new Demo("4"); // 動態物件,未 delete
func();
cout << "main" << endl; // 輸出 main
return 0;
} // main_obj 析構輸出 3,g_obj 析構輸出 2
// p 指向的物件未 delete,~Demo() 未被呼叫
輸出順序:1 → main → 3 → 2。
區域物件按「後進先出」銷毀;全域物件在 main 結束後逆序銷毀;動態物件若無 delete 則永不析構(記憶體洩漏)。
C++ 保證:
new Type → 配置記憶體 → 呼叫建構函數。delete ptr → 呼叫析構函數 → 釋放記憶體。這是 C++ 相較 C 語言 malloc/free 的核心優勢:物件語意與記憶體管理綁定,RAII 機制的基石。
class Base { public: virtual ~Base() {} };
class Derived : public Base { /* ... */ };
Base* p = new Derived;
delete p; // 正確呼叫 Derived::~Derived() 再 Base::~Base()
若 Base 析構非虛擬,delete p 僅呼叫 Base::~Base(),導致 Derived 資源洩漏。基類若有虛擬函數,析構函數必須宣告為 virtual。
obj.~Class() → 僅在 placement new 等極少數場景合法,一般嚴禁。delete[] 陣列 → 未定義行為。析構函數是 C++ 確定性資源管理的核心。遵循「誰配置、誰釋放、在析構函數中釋放」原則,配合 new/delete 配對使用,並為多型基類宣告虛擬析構,即能構建無洩漏、異常安全的 C++ 程式。