HOWTO · C++

在 C++ 顯式呼叫解構函式

本文演示瞭如何在 C++ 中顯式呼叫解構函式

本頁內容

在 C++ 中,使用 obj.~ClassName() 可明確呼叫物件的解構函式。這會結束該物件的生命週期;除非在其儲存空間中建立新物件,否則之後不得再使用它。手動呼叫解構函式並不常見,通常以作用域管理清理,或使用 std::unique_ptr 這類擁有權型別會更安全。

使用 obj.~ClassName() 表示法明確呼叫解構函式

物件生命週期結束時,解構函式會釋放該物件所持有的資源。自動物件離開作用域時,編譯器會自動呼叫解構函式。若指標是由 new 傳回,delete 會先呼叫解構函式,再釋放物件的儲存空間。這些正常的清理方式不應再搭配額外的手動呼叫:若明確解構一個自動物件,之後又讓它離開作用域,就等於試圖再次解構一個生命週期已結束的物件。這是未定義行為,不會可靠地輸出兩則訊息。同樣地,手動解構以 new 建立的物件,之後又對同一個指標使用 delete,也不是有效的清理方式。

在妥善管理的低階程式碼中,明確呼叫解構函式可能有用,例如物件位於呼叫端提供的儲存空間,而程式準備在該處建立另一個物件。以下範例使用 placement new,在位元組緩衝區中開始物件的生命週期,呼叫其解構函式後,再於相同位址建立替代物件。最後一次明確呼叫會結束替代物件的生命週期;位元組緩衝區本身不會自動解構儲存在其中的物件。

呼叫方式是先寫物件運算式,再接上 .~ 與類別名稱:若物件名為 obj,類別為 Widget,寫法就是 obj.~Widget()。解構函式沒有回傳值,也不接受引數。呼叫時仍須遵守存取規則,因此若解構函式是私有的,無權存取它的程式碼就不能呼叫。呼叫解構函式會執行其清理工作,但不會釋放透過配置器或 new 取得的儲存空間;儲存空間管理是另一項責任。

範例中的 alignas(Widget) 會讓位元組緩衝區符合 Widget 所需的對齊方式,而緩衝區大小足以容納一個實例。Placement new 會在既有緩衝區中建構物件,不會另行要求配置儲存空間。第一次明確呼叫會在建立替代物件前結束第一個實例的生命週期;之後也會明確解構替代物件。由於儲存空間是區域位元組陣列,離開作用域時只會結束陣列本身的生命週期,不會呼叫放置其中的 Widget 解構函式。使用這種模式時,程式設計者必須追蹤儲存空間中目前存活的物件,並確保每個建構完成的物件恰好解構一次。

對一般應用程式而言,使用區域變數更簡單:控制流程離開區塊時,包括函式回傳時,變數的解構函式都會執行。若需要動態管理擁有權,std::unique_ptr<Widget> 通常很合適,因為物件的解構會與擁有者的生命週期連動。若物件屬於容器或另一個類別,應讓該擁有者管理解構,而不要手動呼叫成員物件的解構函式。這些方式能讓清理流程從程式結構中清楚呈現,並避免在物件生命週期結束後繼續使用它。

執行此範例:範例的輸出區塊會顯示四行結果;其中的英文標籤依照原樣保留。

#include <iostream>
#include <new>
#include <string>
#include <utility>

class Widget {
 public:
  explicit Widget(std::string name) : name_(std::move(name)) {
    std::cout << "created " << name_ << '\n';
  }

  ~Widget() { std::cout << "destroyed " << name_ << '\n'; }

 private:
  std::string name_;
};

int main() {
  alignas(Widget) unsigned char storage[sizeof(Widget)];
  Widget* obj = ::new (static_cast<void*>(storage)) Widget("first");
  obj->~Widget();
  obj = ::new (static_cast<void*>(storage)) Widget("second");
  obj->~Widget();
}
created first
destroyed first
created second
destroyed second

若程式碼不需要直接管理儲存空間與物件生命週期,讓區域物件自然離開作用域即可。若要管理動態配置物件的擁有權,請優先使用 std::unique_ptr;擁有者離開作用域時,其解構函式會使用 delete。物件解構後,請勿再透過指標存取它;也不要只為了釋放成員資源而手動呼叫解構函式,應改用合適的標準函式庫擁有者或資源包裝器。