在 C++ 中,臨時物件的生命週期管理若不當,會導致解構函式被重複呼叫,造成重複釋放記憶體、程式崩潰等嚴重錯誤。本文透過一個 stack 類別範例,說明臨時物件產生、複製建構、解構時機,並提供三種解決方案。
#include <cstring>
#include <iostream>
using namespace std;
class Stack {
public:
Stack(const char* value);
~Stack();
char* data;
};
Stack::Stack(const char* value) {
if (value) {
data = new char[strlen(value) + 1];
strcpy(data, value);
} else {
data = new char[1];
*data = '\0';
}
cout << "Construct: " << data << endl;
}
Stack::~Stack() {
cout << "Destruct: " << data << endl;
delete[] data;
}
// 測試函式:傳值回傳會產生臨時物件
Stack GetStack() {
return Stack("Temp"); // 回傳臨時物件
}
int main() {
Stack s = GetStack(); // 複製建構 (或移動建構)
cout << "Main end" << endl;
return 0;
}
Construct: Temp
Destruct: Temp // 臨時物件解構
Destruct: Temp // s 解構 → 重複 delete[] → Crash!
GetStack() 回傳 Stack("Temp") 產生臨時物件 (rvalue)。Stack s = GetStack() 以複製建構函式 初始化 s (C++17 前可能經歷兩次複製,C++17 保證複製省略 RVO)。s.data 與臨時物件 data 指向同一記憶體。s 解構再次 delete[] 同一指標 → Double Free / Heap Corruption。Stack(const Stack& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
Stack& operator=(const Stack& other) {
if (this != &other) {
delete[] data;
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
return *this;
}
std::unique_ptr / std::shared_ptr)#include <memory>
class Stack {
std::unique_ptr data;
public:
Stack(const char* v) { /* 配置 unique_ptr */ }
// 無需手寫解構/複製/移動,unique_ptr 自動處理
};
Stack(Stack&& other) noexcept : data(other.data) { other.data = nullptr; }
Stack& operator=(Stack&& other) noexcept {
if (this != &other) { delete[] data; data = other.data; other.data = nullptr; }
return *this;
}
配合 return Stack("Temp"),編譯器優先使用移動建構,零開銷轉移擁有權。
std::string、std::vector、智慧指標等標準庫容器,避免手動 new/delete。/Wall、-Wextra) 捕捉缺失複製建構函式的類別。