iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
自我挑戰組

Effective C++ 讀書筆記系列 第 19

[Day 19] Use objects to manage resources

  • 分享至 

  • xImage
  •  

前言

終於要開始第三章了~ 第三章總共有5個守則,馬上就來看看第一則吧!

RAII- Resource Acquisition Is Initialization

第一個守則是:

Use obejcts to manage resources

假設現在我們想要創建一個物件,並在使用完畢後把他刪除,我們可能會這樣寫:

void f()
{
    Investment *pInv = createInvestment();
    ...
    delete pInv;
}

這種寫法有一些潛在危險性,讓這個資源沒有被完全delete掉。例如:在...裡面有先return了,不是每次都可以返回;或者是中途會報exception,一樣執行不到最後;又或者delete是寫在loop裡面,在執行到之前有gotocontinue把它跳掉......。雖然在寫的當下,你可能考慮到全部的情況,可以避免以上的所有情形,然而,久了之後你不能確定未來的你或其他人會不會忽略了這些考量,而動到了原本的設計,導致以上狀況有可能發生,若只靠原本的這個function設計並不可靠。

那要確保createInvestment()產生的東西都可以被刪除,可以怎麼做呢?我們可以把它放到一個物件裡,並把刪除它的操作放到它的destructor,這樣就可以確保離開f()的時候它會被自動刪除。而auto_ptr就是為此情形量身打造的,它屬於一種smart pointer,destructor會自動對它指向的東西call delete。它的用法如下:

void f()
{
    std::auto_ptr<Investment> pInv(createInvestment());
    ...
}

這個用法顯示了兩個使用物件來控制resource的重點:

  1. Resources are acquired and immediately truned over to resource-managing objects.:(我稱它創出即物件來記憶)。什麼意思呢?在上面的例子中,createInvestment創出來的資源馬上用來初始化auto_ptr並由它來管理,這個用物件來管理資源的方式又稱為RAII─Resource Acquisition Is Initialization ,因為時常會在同一個statement裡面同時取得資源並把這個資源拿來初始化resource-managing object;有時候可能不是初始化,而是assign的操作,但總之就是取得後轉移到resource-managing object上。
  2. Resource-managing objects use their destructors to ensure that resources are released.:(我稱它dtor管釋放來記憶)。因為destructor會在物件被destroyed的時候自動執行,就可以確保他們都有被清除,無論中間是怎麼離開的。

也由於auto_ptr會自動delete的特性,我們不能讓多個auto_ptr指向同一個物件,否則物件就會被重複刪除;因而auto_ptr有個特別行為就是copy它的時候(那兩個copying functions),就會把它指向的東西設為null,以保證只有一個人指向它。

這邊不就多再說明auto_ptr什麼時候會被設成null的行為了,因為auto_ptr已經被C++11放棄了~~就因為它上面的這些比較難以掌控的行為,C++11後就有了unique_ptr。差別在哪裡呢?可以參考下面的一些參考資料。這邊簡單來說就是:當你物件被unique_ptr指之後,它就不能再被copy,auto_ptr不會報錯,unique_ptr會。顧名思義,用unique_ptr來指向的物件,它就是"unique"的,不期望它還被別人使用;如果別人也會指向它,那就請改用下面會提到的─shared_ptr

RCSP- reference-counting smart pointer

除了auto_ptr,還有RCSP- Reference-counting smart pointer型的smart pointer。它會記錄這個物件被指向多少次,而只有在所有指向它的物件都被destroyed掉後才去delete這個物件。shared_ptr就是這種應用。你可以這樣寫(書中原本的寫法shared_ptr還在TR1中,但C++11後已納入標準函式庫,因此下方寫沒有TR1的版本)

void f()
{
    std::shared_ptr<Investment> pInv(createInvestment());
}

使用它,就可以正常的call copy functions沒有問題,例如這樣:

void f()
{
    std::shared_ptr<Investment> pInv1(createInvestment());
    std::shared_ptr<Investment> pInv2(pInv1);
    pInv1 = pInv2;
}

而對於dynamically allocated array,就沒有這種smart pointer的應用(就是如果你指向的是一個array,smart pointer在delete的時候並不是delete[]!!!)。但通常其實沒有需要,大部分可用vectorstring來取代。

總之這個守則想表達的就是:如果在destructor以外去手動delete,你可能在錯誤的道路上。有時候可能不一定能用smart pointer來達成,但你可以自行創建一個類似的object。當然這也有其他要注意的事項,後面的守則會再提到。

總結

貼心重點提醒:

  • To prevent resource leaks, use RAII obejcts that acquire resources in their constructors and release them in their destructors.
  • Two commonly useful RAII classes are shared_ptr and auto_ptr. shared_ptr is usually the better choice, because its behavior when copied is intuitive. Copying an auto_ptr sets it to null.

注意:第二點改用unique_ptr

參考資料

  • auto_ptr: 實際上,C++ 11 提供了 unique_ptr、shared_ptr 等類別模版,可以根據不同資源管理需求來選用,因此不該再使用 auto_ptr
  • C++11新特性-智能指针: unique_ptr 由 C++11 引入,旨在替代不安全的 auto_ptr。
  • C++ Technical Report 1:C++ Technical Report 1(TR1)是ISO/IEC TR 19768, C++ Library Extensions(函式庫擴充)的一般名稱。TR1是一份檔案,內容提出了對C++標準函式庫的追加項目。這些追加項目包括了正規表示式、智慧型指標、雜湊表、亂數生成器等。TR1自己並非標準,它是一份草稿檔案。然而它所提出的項目大多數已成為的C++11及之後版本的官方標準的一部分。這份檔案的目標在於「為擴充的C++標準函式庫建立更為廣泛的現成實作品」。
  • 智能指针之auto_ptr、unique_ptr、shared_ptr

上一篇
[Day 18] Copy all parts of an object (2)
下一篇
[Day 20] Think carefully about copying behavior in resource-managing classes
系列文
Effective C++ 讀書筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言