Effective C 讀書筆記7

2021-06-06 18:22:38 字數 4715 閱讀 8333

條款23:寧以non-member,non-friend替換member函式

請記住:寧可拿non-member non-friend函式替換member函式,這樣做可以增加封裝性,包裹彈性和機能擴充套件性

條款24:若所有引數皆需型別轉換,請為此採用non-member函式

請記住:如果你需要為某個函式的所有引數(包括this指標所指的那個隱喻引數)進行型別轉換,那麼這個函式必須是個non-member

條款25:考慮寫出乙個不拋一場的swap函式

5 實現

1)太快定義變數可能造成效率上的拖延

2)過度使用轉型可能導致**變慢又難維護,又招來微妙難解的錯誤

3)返回物件內部資料之號碼牌(handles)可能會破壞封裝並留給客戶虛吊號碼牌(dangling handles);

4)未考慮一場帶來的衝擊則可能導致資源洩漏和資料敗壞;

5)過度熱心的inline可能引起**膨脹;

6)過度耦合(coupling)則可能導致讓人不滿意的冗長build time

條款26:盡可能延後變數定義的出現時間

只要你定義了乙個變數而其型別帶有乙個建構函式和析構函式,那麼當程式的控制流到達這個變數定義時,你便得承受構造成本;當這個變數離開作用域時,你便得承受析構成本。或許你會認為,你不可能定義乙個不使用的變數,那麼請看:

std::string encryptpassword(const std::string& password)

return encrypted;

}

物件encrypted在此函式中並非完全未被使用,但如果有個異常被丟出,它就真的沒被使用。所以應該這樣:

std::string encryptpassword(const std::string& password)

string encrypted;

//...

return encrypted;

}

但是這段**仍然不夠穠纖合度,因為encrypted雖然獲得定義但卻無任何實參作為初值,這意味著呼叫的是其預設建構函式,條款4解釋了為什麼通過預設建構函式構造出乙個物件然後對它賦值比直接在構造時指定初值的效率差。

std::string encryptpassword(const std::string& password)

std::string encryptpassword(const std::string& password)

這讓我們聯想起本條款所謂盡可能延後的真正意義。你不只應該延後變數的定義,直到非得使用該變數的前一刻為止,甚至應該嘗試延後這份定義直到能夠個他初值實參為止。如果這樣,不僅能夠避免構造和析構非必要物件,還可以避免無意義的預設構造行為。

關於迴圈:

請記住:

盡可能延後變數定義式的出現。這樣做可增加程式的清晰度並改善程式效率。

條款27: 盡量少做轉型動作

cast破壞了型別系統,那可能導致任何種類的麻煩,有些容易辨識,有些非常隱晦。

c風格的cast動作看起來像這樣:

(t)expression

函式風格的轉型動作看起來像這樣:

t(expression) (????????????)

兩種形式並無差別,純粹只是小括號的擺放位置不同而已。c++提供四種新式轉型:

1.const_cast通常被用來將物件的常量性移除。

2.dynamic_cast主要用來執行安全向下轉型,也就是用來決定某物件是否歸屬繼承體系中的某個型別。它是唯一無法由舊式語法執行的動作,也是唯一可能消耗重大執行成本的轉型動作。

3.reinterpret_cast意圖執行低階轉型,實際動作可能取決於編譯器,這也就表示它不可移植。例如將乙個pointer to int轉型為乙個int。這一類轉型的低階**以外很少見。本書只使用一次,那是在討論如何針對原始記憶體寫出乙個除錯用的分配器時,見條款50.

4.static_cast用來強迫隱士轉換,例如將non-const物件轉為const物件,或將int轉為double等等。它也可以用來執行上述多種轉換的反向轉換,例如將void*轉為typed指標,將pointer-to-base轉為pinter-to-derived.但它無法將const轉為non-const.

class base{};

class derived: public base{};

derived d;

base* pb = &d;

這裡我們不過是建立乙個base class指標指向乙個derived class物件,但有時候上述的兩個指標值並不相同。這種情況下會有個偏移量在執行期間被施行於derived*指標身上,用以取得正確的base*指標值。

上述例子表明,單一物件可能擁有乙個以上的位址(例如當以base*指向它時的位址和以derived*指向它時的位址)。??????????

另乙個關於轉型的有趣事情是:我們可能容易寫出某些似是而非的**,例如許多應用框架都要求derived classes內的virtual函式**的第乙個動作就先呼叫base class的對應函式。以下**看起來對,但是是錯誤的:

class window

//...

};class specialwindow: public window

};

恐怕你沒想到的是,它呼叫的並不是當前物件上的函式,而是稍早轉型動作所建立的乙個「*this物件之base class成分」的暫時副本身上的onresize(譯註:函式就是函式,成員函式只有乙份,呼叫起哪個物件身上的函式有什麼關係呢?關鍵在於成員函式都有個隱藏的this指標,會因此影響函式操作的資料)(???????????)。所以如果window::onresize修改了物件內容,當前物件其實沒被改動,改動的只是副本。然而specialwindow::onresize內如果也修改物件,當前物件真的會被改動。這樣的結果是:其base class成分的更改沒有落實,而derived class成分的更改倒是落實了。(整段都沒看懂)

所以應該這樣寫:

class specialwindow: public window

};

之所以需要dynamic_cast,通常是因為你想在乙個你認定為derived class物件身上執行derived class操作函式,但你手上卻只有乙個指向base的pointer或reference。兩個方法可以避免這個問題:

方法一:使用容器並在其中直接指向derived class物件的指標(通常是只能指標):

不應該這樣做:

class window{};

class specialwindow: public window;

typedef std::vector> vpw;

vpw winptrs;

for(vpw::iterator iter = winptrs.begin(); iter != winptrs.end(); ++iter)

}

應該這樣做:

typedef std::vector> vpsw;

vpsw winptrs;

for(vpsw::iterator iter = winptrs.begin(); iter != winptrs.end(); ++iter)

方法二:該方法可以讓你通過base class介面處理所有可能之各種window派生類,那就是在base class內提供virtual函式做你想對各個window派生類做的事。

class window  //do nothing

//...

};class specialwindow: public window; //do something };

typedef std::vector> vpw;

vpw winptrs;

for(vpw::iterator iter = winptrs.begin(); iter != winptrs.end(); iter++)

請記住:

1.如果可以,盡量避免cast,特別是在注重效率的**中避免dynamic_cast。如果有個設計需要cast,試著發展無需轉型的替代設計。

2.如果轉型是必須的,試著將它隱藏在某個函式背後。客戶隨後可以呼叫該函式,而不需要將轉型放進他們自己的**中。

3.寧可使用c++-style轉型,不要使用舊式轉型,前者容易識別出來,而且也比較有著分門別類的職掌。

《effective C 》讀書筆記

1,c 關鍵字explicit c 中,乙個引數的 建構函式 或者除了第乙個引數外其餘引數都有預設值的多參建構函式 承擔了兩個角色。1 是個 構造器,2 是個預設且隱含的型別轉換操作符 所以,有時候在我們寫下如 aaa 這樣的 且恰好 的型別正好是aaa單引數構造器的引數型別,這時候 編譯器就自動呼...

Effective C 讀書筆記

一 讓自己習慣c 1 條款01 視c 為聯邦語言 c 的組成可分為四部分 1.c c 仍然以c語言為基礎。區塊 語句 預處理 內建資料型別 陣列 指標等都來自c。2.object oriented c c with classes所訴說的 classes 包括構造和析構 封裝 繼承 多型 virtu...

讀書筆記 Effective C

部分條款過於深奧,部分條款已了然於心,僅記錄當下所識所學 對於常量巨集定義,最好用const代替 define 對於函式巨集定義,最好用inline代替 define include ifdef ifndef仍被需要 內建物件記得手動初始化 使用成員初始列替換賦值操作 以local static替換...