天天看點

Effective c++ 讀書筆記

  1. class Widget

    {

    public:

    Widget();//default構造函數

    Widget( constWidget& rhs );//copy構造函數

    Widget& operator=( constWidget& rhs);//copy assignment 操作符

    };

Widget w1;//調用default構造函數

Widget w2(w1);//調用copy構造函數

w1 = w2;//調用copy assignment 操作符

當你看到指派符号時請小心,因為“=”文法也用來調用copy構造函數

Widget w3 = w2;//調用copy構造函數

“copy構造” 很容易和“copy指派”有所差別。如果一個新對象被定義(如以上語句中的w3)

, 一定會有個構造函數被調用,不可能調用指派操作。如果沒有新對象被定義

(如上述的“w1 = w2”語句),就不會有構造函數被調用,那麼當然就是指派操作被調用。

條款1:視c++為一個語言聯邦

四個次語言:

* C;說到底C++仍是以C為基礎

* Object-Oriented C++;這也是C with classes 所訴求的

* Template C++;這是C++泛型程式設計的部分

* STL; STL是個Template程式庫

條款2:盡量以const , enum, inline 替換 #define

這個條款或許改為“甯可以編譯器替換預編譯器”比較好。

記住:

* 對于單純常量,最好以const對象或enum替換#define

* 對于形似函數的宏(macro),最好改用inline函數替換#define

條款3: 盡可能使用const

const指定 一個“不該被改動的”對象,編譯器會強制實施這項限制。

char* p = greeting;//non_const pointer, non_const data

const char* p = greeting;//non_const pointer, const data

char* const p = greeting; //const pointer, non_const data

const char* const p = greeting;//const pointer, const data

如果關鍵字const 出現在星号左邊,表示被指物是常量;如果出現在星号右邊, 表示指針本身是常量;

如果出現在星号兩邊,表示被指物和指針兩者都是常量。

void f1(const Widget* pw); //f1獲得一個指針,指向一個常量(不變的)Widget對象

void f2(Widget const * p2);//f2 也是

上述兩種寫法意義一樣。

STL疊代器是以指針為根據塑模出來,是以疊代器的作用就像個T* 指針

也就是說iterator 相當于T*;

如果希望疊代器所指的東西不可被改變(即希望STL模拟一個const T* 指針),就用const_iterator;

class TextBlock

const char& operator[](std::size_t position) const //operator[]for const 對象

return text[position];

}

char& operator[](std::size_t position) //operator[]for non_const 對象

private:

std::string text;

}

const_cast (expression)

const_cast轉換符是用來移除變量的const或volatile限定符。除了const 或volatile修飾之外, type_id和expression的類型是一樣的。

一、常量指針被轉化成非常量的指針,并且仍然指向原來的對象;

二、常量引用被轉換成非常量的引用,并且仍然指向原來的對象;

三、const_cast一般用于修改指針。如const char *p形式。

static_cast < type-id > ( expression )

該運算符把expression轉換為type-id類型,但沒有運作時類型檢查來保證轉換的安全性。它主要有如下幾種用法:

①用于類層次結構中基類(父類)和派生類(子類)之間指針或引用的轉換。

進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的;

進行下行轉換(把基類指針或引用轉換成派生類表示)時,由于沒有動态類型檢查,是以是不安全的。

②用于基本資料類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。

③把空指針轉換成目标類型的空指針。

④把任何類型的表達式轉換成void類型。

注意:static_cast不能轉換掉expression的const、volatile、或者__unaligned屬性。

條款4:确定對象在使用前已被初始化

良好習慣規定:在初始化清單中列出所有的成員變量;

c++成員初始化次序

1. 基類早于派生類初始化;

2. 類的成員變量總是以其聲明的順序被初始化

所謂static對象,其壽命從被構造出來到程式結束為止,是以,stack和heap_based對象都可以排除。這些對象包括global對象、定義域namespace作用域内的對象、在class内、在函數内、以及在file作用域内被聲明為static的對象。函數内的static對象稱為local static對象(因為它們對函數而言是local的),其他static對象稱為non_local static對象。程式結束時static對象會被自動銷毀,也就是它們的析構函數會在main()結束時自動調用。

c++對于定義在不同編譯單元内的non_local static對象的初始化次序

并沒有明确定義。

一個小小的設計可以消除這個問題:将每個non_local static對象搬到自己的專屬函數内(該對象在此函數内被聲明為static)。這些函數傳回reference指向它所含的對象。然後使用者調用這些函數,而不直接使用這些對象。換句話說,這些non_local static對象被local static對象替換了。這是singleton模式常用的一個實作手法。

1. 對于内置對象進行手工初始化,c++不保證初始化它們。、

2. 構造函數最好使用初始化清單(member initialization list),而不要在構造函數本體内使用指派操作(assignment)。初始化清單列出的成員變量,其排列次序應該和它們在class内聲明的次序相同。

3. 為免除“跨編譯單元之初始化次序”的問題,請以local static對象代碼non_local static對象。

條款5:了解C++默默編寫并調用了哪些函數

編譯器可以暗自為class建立 default構造函數、copy構造函數、析構函數和copy assignment操作符。并且這個析構函數是非virtual的。

條款6:如果不想使用編譯器自動生成的函數,就該明确拒絕

為駁回編譯器自動(暗自)提供的機能,可以将相應的成員函數聲明為private并且不予實作。并将這個類作為基類。

條款7:為多态基類聲明virtual析構函數

c++明确指出:當一個derived class 經由一個base class指針被删除,而該base class 帶着一個non_virtual 析構函數,其結果未有定義,實際執行時通常發生的是對象的derived 成分沒有被銷毀。進而造成記憶體洩露的問題。解決這個問題的方法就是把基類的析構函數聲明為virtual。

1. 帶多态性質的基類應該聲明一個virtual析構函數。如果class帶有任何virtual函數,那麼就應該聲明一個virtual析構函數。

2. class設計的目的如果不是為了作為基類使用,或不是為了具備多态性,就不應該聲明virtual析構函數。這樣節省記憶體開支。

條款8:别讓異常逃離析構函數

1.析構函數絕對不要吐出異常。如果一個被析構函數調用的函數可能抛出異常,析構函數應該捕捉任何異常,然後吞下它們(不傳播)或結束程式。

2. 如果客戶需要對某個操作函數運作期間抛出的異常做出反應,那麼class應該提供一個普通函數而非在析構函數中執行該操作。

條款9:絕不在構造函數和析構函數中調用virtual函數

構造和析構期間不要調用virtual函數,因為這類調用從不下降至派生類(比起目前執行構造和析構函數的那層)

條款10:令operator = (指派操作符)傳回一個reference to *this

Widget& operator=(const Widget& ths)

return *this;

條款11: 在operator=中處理“自我指派”

1. 確定當對象自我指派時operator=有良好行為。其中技術包括比較“來源對象”和“目标對象”的位址,精心周到的語句順序,以及copy_and_swap

2. 确定任何函數如果操作一個以上的對象,而其中多個對象是同一個對象時,其行為仍然正确。

條款12:複制對象時勿忘複制其每一個成分

1. 當編寫一個copying函數,請確定(1)複制其每一個成員變量,(2)調用所有父類适當的copying函數

2. 令copy assignment操作符調用copy構造函數是不合理的,因為這就像構造一個已經存在的對象

3. 令copy構造函數調用copy assignment操作符同樣不合理,構造函數用來初始化新對象,而copy assignment操作符隻能施行與已初始化的對象。

4. 不要以某個copying函數實作另一個copying函數,應該将共同機制放在第三個函數中,并由兩個copying函數共同調用。

第三章 資源管理

條款13:以對象管理資源

1. 把資源放進對象内,我們便可以依賴c++“析構函數自動調用機制”確定資源被釋放

2. auto_ptr 被銷毀時會自動删除它所指向之物,是以一定要注意别讓多個auto_ptr指向同一個對象。

3. 與引用計數型智能指針不同的,auto_ptr要求其對“裸”指針的完全占有性。也就是說一個“裸”指針不能同時被兩個以上的auto_ptr所擁有。那麼,在拷貝構造或指派操作時,我們必須作特殊的處理來保證這個特性。auto_ptr的做法是“所有權轉移”,即拷貝或指派的源對象将失去對“裸”指針的所有權,是以,與一般拷貝構造函數,指派函數不同, auto_ptr的拷貝構造函數,指派函數的參數為引用而不是常引用(const reference).當然,一個auto_ptr也不能同時擁有兩個以上的“裸”指針,是以,拷貝或指派的目标對象将先釋放其原來所擁有的對象

4. STL容不得 auto_ptr

5. 引用計數型智慧指針 reference-counting smart pointer (RCSP)

6. std::tr1::shared_ptr 就是RCSP

7. RAII (Resource Acquisition Is Initialization),也稱為“資源擷取就是初始化”,是C++語言的一種管理資源、避免洩漏的慣用法。C++标準保證任何情況下,已構造的對象最終會銷毀,即它的析構函數最終會被調用。簡單的說,RAII 的做法是使用一個對象,在其構造時擷取資源,在對象生命期控制對資源的通路使之始終保持有效,最後在對象析構的時候釋放資源。

8. 為防止資源洩漏,請使用RAII對象,它們在構造函數中獲得資源并在析構函數中釋放資源

9. 兩個常被使用的RAII classes 分别是tr1::shared_ptr 和auto_ptr。前者通常是較佳選擇,因為其copy行為比較直覺。若選擇auto_ptr,複制動作會使它(被複制物)指向NULL

條款14:在資源管理類中小心coping行為

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

1.對原始資料的通路可能經由顯式轉換或隐式轉換,一般而言,顯式轉換比較安全,但隐式轉換對客戶比較友善

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

1. 當使用new(也就是通過new動态生成一個對象),有兩件事情發生。第一,記憶體被配置設定出來(通過名為operator new的函數)。第二,針對此記憶體會有一個或多個構造函數被調用。

2. 當使用delete,也有兩件事情發生:針對此記憶體有一個或多個析構函數被調用,然後記憶體才被釋放(通過operator delete的函數)

3. 如果你在new表達式中使用[],必須在相應的delete表達式中也使用[].如果在new表達式中不使用[],一定不要在相應的delete表達式中使用[]

條款17:以獨立語句将newed對象置入隻能指針

1. 以獨立語句将newed對象存儲于(置入)智能指針。如果不這樣做,一旦異常被抛出,有可能導緻難以察覺的資源洩漏

第四章

設計與聲明

條款18:讓接口容易被正确使用,不易被誤用

1. 好的接口很容易被正确使用,不容易被誤用。你應該在你的所有接口中努力達成這些性質。

2. “促進正确使用”的辦法包括接口的一緻性,以及與内置類型的行為相容

3. “阻止誤用”的辦法包括建立新類型、限制類型上的操作,束縛對象值,以及消除客戶的資源管理責任

4. tr1::shared_ptr 支援定制型删除器,這可防範dll 問題,可被用來自動解除互斥鎖等等

條款19:設計class猶如設計type

條款20:甯以pass_by_reference_to_const替換pass_by_value

1. 盡量以pass-by-reference-to-const替換pass-by-value. 前者通常比較高效,并可避免切割問題(slicing problem)

2. 以上規則并不适用于内置類型,以及STL的疊代器和函數對象。對它們而言,pass-by-value比較适當

條款21:必須傳回對象時,别妄想傳回其reference

1. 絕不要傳回pointer或reference指向一個local stack對象,或傳回reference指向一個heap-allocated對象,或傳回pointer或reference指向一個local static對象而有可能同時需要多個這樣的對象。