C++ 筆記本 -- auto_ptr

auto_ptr

auto_ptr 是 C++ 用來保護動態配置的物件用的。利用 auto_ptr 包住一個指向該物件的指標,則該物件在 scope 結束時一定會被 delete,不論是正常結束,或是因為例外而結束。因此,在有可能發生例外的地方,所有動態配置的物件都應該用 auto_ptr 保護起來。

auto_ptr 原則上是包含一個指向動態配置的物件的指標。當 auto_ptr 的生命期結束時(即離開 scope)則將該物件 delete 掉。觀念上是非常簡單的。不過,問題在於,由於不能有兩個 auto_ptr 物件的指標指向同一個物件,否則會造成重覆的 delete。所以當 auto_ptr 在進行指派的時候(例如 p = q),就需要進行一些處理。

第一個版本的 auto_ptr,所採取的處理方式是:在 operator= 中,把指標傳給下一個 auto_ptr 物件,並將目前的 auto_ptr 物件的指標設成 NULL。這樣一來,隨時都會只有一個 auto_ptr 物件具有指向物件的指標,而其它的都會是 NULL。因此,這樣就不會有重覆的 delete 問題。由於指派會造成修改,所以 const auto_ptr 應該不能指派給別的 auto_ptr。因此,auto_ptr 不具有 const 的 copy constructor。然而,這樣造成一個問題:由於把 auto_ptr 傳到別的函式內,或是別的函式要傳回 auto_ptr,都會需要經過暫時變數,也就需要 const reference 的 copy constructor,因此第一版的 auto_ptr 無法傳到函式內,也無法從函式中傳回。

為了處理這個問題,第二版的 auto_ptr 利用一個旗標(通常用 bool 型態)來記錄目前這個 auto_ptr 物件是否「擁有」這個指標。這個旗標被設為 mutable,因此它可以具有 const 的 copy constructor。而 operator= 和 copy constructor 都設計成同時只有一個 auto_ptr 物件「擁有」該指標。在物件的生命期結束時,只有「擁有」該指標的 auto_ptr 物件才進行 delete 動作。這樣一來,就可以把 auto_ptr 傳入函式內,或從函式中傳出了。

然而,這樣還是有一些問題。首先,一個 auto_ptr 即使不再「擁有」該指標,仍然可以使用。這就導致很多可能的潛在問題。例如,如果兩個 auto_ptr 在不同(但重疊)的 scope 中,則有可能其中一個 auto_ptr 的生命期先結束,並因為它「擁有」該指標而將該物件刪除。這時,生命期較長的 auto_ptr 物件中的指標就變成是一個 dangling pointer。這會造成很多問題。另外這個 auto_ptr 物件所需要的額外空間也是不理想的。

為了解決這些問題,第三版(即最終版)的 auto_ptr 引進了一個「隱藏」的型態 auto_ptr_ref。這個型態是一個簡單的 wrapper,可以存放 auto_ptr 所存放的指標。在原先 INCITS/ISO/IEC 14882:1998(E) 的標準中,auto_ptr_ref 是放在 auto_ptr 類別的 private 區域中,但後來被發現這樣會導致問題。這是因為 auto_ptr 必須能容許不同型態之間的轉換(例如 auto_ptr<Base> = auto_ptr<Derived>),因此它需要 member template function 來處理這些轉換。然而,如果 auto_ptr_ref 被放在 auto_ptrprivate 區域中,則 auto_ptr_ref<Base>auto_ptr_ref<Derived> 將不具任何關係,當然無法轉換。因此,實際上 auto_ptr_ref 必須放在外面。

auto_ptr_ref 是怎麼運作的呢?第三版的 auto_ptr 原則上和第一版相似。它也是在指派時會將原來的指標設成 NULL,同時也不具有 const 的 copy constructor。為了讓傳入函式/從函式中傳出能運作,在 auto_ptr 中提供了以下的 copy constructor:

和 type cast:

在函式傳回 auto_ptr<T> 時,就可以透過這個轉型和 copy constructor 成功指定給另一個 auto_ptr 物件。同樣的情形也適用在直接將傳回值傳入另一函式中的情形。例如:

在傳回值需要轉換型別時(例如 BaseDerived),則透過以下的 copy constructor 和轉型:

在傳入函式時若需要轉換型別,則需透過以下的轉型(不需要 copy constructor):

不過,雖然 auto_ptr 的細節很複雜,但是第二版和第三版的用法幾乎是完全相同的。在第二版的 auto_ptr 中,雖然 auto_ptrrelease 之後(例如被指派到別的 auto_ptr)還是可以繼續使用,但是原本就不鼓勵這樣使用。而在第三版中則是完全不能使用了(因為其指標已經被設為 NULL)。如果之前使用第二版 auto_ptr 的程式沒有這種不當的使用的話,則轉換到第三版應該不會有任何問題。而且第三版的 auto_ptr 也可以透過 null pointer 抓出這種不當的使用方式。

參考資料

  1. auto_ptr update
  2. INCITS/ISO/IEC 14882:1998(E) Programming Language -- C++

Back


Copyright(c) 2002 Ping-Che Chen