《Effective C++》第3章 資源管理(2)-讀書筆記

來源:互聯網
上載者:User

標籤:

章節回顧:

《Effective C++》第1章 讓自己習慣C++-讀書筆記

《Effective C++》第2章 構造/析構/賦值運算(1)-讀書筆記

《Effective C++》第2章 構造/析構/賦值運算(2)-讀書筆記

《Effective C++》第3章 資源管理(1)-讀書筆記

《Effective C++》第3章 資源管理(2)-讀書筆記

《Effective C++》第8章 定製new和delete-讀書筆記

 

條款15:在資源管理類中提供對原始資源的訪問

許多API直接指涉資源,所以除非你永遠不用它們,否則都會繞過資源管理對象直接存取原始資源。假設使用tr1::shared_ptr管理對象。

std::tr1::shared_ptr<Investment> pInv(createInvestment());

函數daysHeld的聲明是這樣的:

int daysHeld(const Investment *pi);

下面這種調用方式,肯定是錯誤的:

int days = daysHeld(pInv);        //錯誤

因為函數需要的是指標,你傳遞是一個tr1::shared_ptr<Investment>對象。所以你需要一個函數將RAII對象轉換為所內含的原始資源。有兩種方法:隱式轉換和顯示轉換。

(1)顯示轉換

tr1::shared_ptr和auto_ptr都提供了一個成員函數get返回內部的原始指標,這是顯式轉換。

int days = daysHeld(pInv.get());    //好的,沒有問題

(2)隱式轉換

tr1::shared_ptr和auto_ptr都重載了操作符operator->和operator*,這樣就允許隱式轉換到原始指標。舉例:假設Investment類有個成員函數bool isTaxFree() const;那麼下面的調用是OK的:

bool taxable1 = !(pInv->isTaxFree());        //好的,沒有問題bool taxable2 = !((*pInv).isTaxFree());        //好的,沒有問題

現在的問題是,需要原始指標的地方(例如,函數形參),如何以智能指標代替。解決方案是:提供一個隱式轉換函式。下面舉個字型類的例子:

FontHandle getFont();                    //取得字型控制代碼void releaseFont(FontHandle fh);        //釋放控制代碼class Font{public:    explicit Font(FontHandle fh) : f(fh){}    ~Font()    {        releaseFont(f);    }private:    FontHandle f;};

如果C API處理的是FontHandle而不是Font對象,當然你可以像tr1::shared_ptr和auto_ptr那樣提供一個get()函數:

FontHandle get() const { return f; }    //顯示轉換函式

這樣是可以的,但客戶還是覺得麻煩,這時候定義一個隱式轉換函式是必須的。

class Font{public:    ...    operator FontHandle() const { return f; }    ...};

注意:假設你已經知道了隱式轉換函式的用法。例如:必須定義為成員函數,不允許轉換為數組和函數類型等。

完成了以上工作,對於下面這個函數的調用是OK的:

void changeFontSize(FontHandle f, int newSize);Font f(getFont());int newFontSize;changeFontSize(f, newFontSize);        //好的,Font隱式轉換為FontHandle了。

隱式類型轉換也增加了一種風險。例如有以下代碼:

Font f1(getFont());FontHandle f2 = f1;        //將Font錯寫成FontHandle了,編譯仍然通過。

f1被隱式轉換為FontHandle,這時f1和f2共同管理某個資源,f1被銷毀,字型釋放,這時候你可以想象f2的狀態(原諒我這個詞我不會說),再銷毀f2,必然會造成運行錯誤。通常提供一個顯示轉換get函數是比較好的,因為它可以避免非故意的類型轉換的錯誤,這種錯誤估計會耗費你很長的調試時間(我遇到過的情況)。

請記住:

(1)有些API要求訪問原始資源,所以每一個RAII class應該提供一個“取得其所管理的資源”的辦法。

(2)對原始資源的訪問可以通過顯式轉換或隱式轉換。一般顯式轉換比較安全,但隱式轉換對客戶比較方便。

 

條款16:成對使用new和delete時要採取相同形式

我相信你肯定一眼看出以下代碼的問題:

std::string *stringArray = new std::string[100];delete stringArray;

程式行為是未定義的。stringArray所含的100個string對象,99個可能沒被刪除,因為它們的解構函式沒被調用。

delete必須要知道的是:刪除的記憶體有多少個對象,決定了調用多少個解構函式。

單一對象和數組的記憶體布局肯定是不同的,數組佔用的記憶體也許包含一個“數組大小”的記錄。(編譯器乾的事)你可以告訴編譯器刪除的是數組還是單一對象:

std::string *stringPtr1 = new std::string;std::string *stringPtr2 = new std::string[100];delete stringPtr1;        //刪除一個對象delete [] stringPtr2;    //刪除一個數組

這個規則很簡單,但有一點需要注意:

typedef std::string AddressLines[4];std::string *pal = new AddressLines;delete pal;                //不好,行為未定義。delete [] pal;            //很好。

所以,最好不要對數組做typedef。

請記住:如果new運算式中使用了[],必須在相應的delete運算式中使用[];如果new運算式中不使用[],一定不要在相應的delete運算式中使用[]。

 

條款17:以獨立語句將newed對象置入智能指標

假設有以下函數,具體含義我們可以先忽略:

int priority();void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);

先說明一點的是,當你以下面形式調用processWidget時,肯定是錯誤的:

processWidget(new Widget, priority());

編譯出錯。tr1::shared_ptr建構函式需要一個原始指標,但該建構函式是explicit,即禁止隱式類型轉換的。所以,正常情況你應該這樣調用:

processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());    //好的,沒有問題

以這種調用方式,執行函數體之前,有三個工作要做:

(1)調用priority。

(2)執行"new Widget"。

(3)調用tr1::shared_ptr建構函式

可以肯定的是(2)在(3)前面執行,但(1)的執行次序不能確定。假設(1)在第二個被執行,則如果調用priority出現異常,new Widget返回的指標將會遺失,因為還未執行tr1::shared_ptr的建構函式。所以,發生了資源流失。

避免這類問題很簡單:使用分離語句。

std::tr1::shared_ptr<Widget> pw(new Widget);processWidget(pw, priority());                //絕不會造成泄露

請記住:以獨立語句將newdObject Storage Service於智能指標內。如果不這樣做,一旦異常被拋出,有可能造成難以察覺的資源流失。

《Effective C++》第3章 資源管理(2)-讀書筆記

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.