Effective C 條款24 第4章

2021-07-05 02:49:07 字數 3110 閱讀 3705

令 class 支援隱式型別轉換通常是個糟糕的主意,當然這條規則有其例外,最常見的例外是在建立數值型別時.假設設計乙個 class 用來表現有理數,允許整數"隱式轉換"為有理數似乎頗為合理.的確,它並不比c++內建從 int 至 double 的轉換來得不合理.假設這樣開始rational class:

class rational ;
如果想支援算數運算諸如加法,乘法等等,但不確定是否由member函式,non-member函式,如果可能的話由non-member friend 函式來實現它們.條款23曾反直覺地主張將函式放進相關 class 內有時會與物件導向守則發生矛盾.對於先研究一下將 operator*放在 class 內,寫成rational成員函式:

class rational ;
這個設計使得能夠將兩個有理數以輕鬆的方式相乘:

rational oneeighth(1, 8);

rational onehalf(1, 2);

rational result = onehalf * oneeighth; // ok

result = result * oneeighth; // ok

但是如果嘗試混合式演算法,會發現只有一半行得通:

result = onehalf * 2;                        // ok

result = 2 * onehalf; // error

以對應的函式形式重寫上述兩個式子,問題所在便一目了然了:

result = onehalf.operator*(2);                // ok

result = 2.operator*(onehalf); // error

onehalf是乙個內含 operator* 的函式的 class 的物件,所以編譯器會呼叫該函式.然而整數2並沒有相應的 class,也就是沒有 operator* 成員函式.編譯器也會尋找可被以下這般呼叫的non-member operator*(也就是在命名空間內或在global作用域內):

result = operator*(2, onehalf);                // error
但本例並不存在這樣乙個接受 int 和 rational作為引數的non-member operator*,因此查詢失敗.

再看看先前成功的那個呼叫.注意其第二引數是整數2,但rational::operator* 需要的實參卻是個rational物件.這裡發生了什麼事?

為什麼2在這裡可被接受,在另乙個呼叫中卻不被接受?

因為這裡發生了所謂隱式型別轉換(implicit type conversion).編譯器知道在傳遞乙個 int,而函式需要的是rational:但它也知道只要呼叫rational建構函式並賦予所提供的 int,就可以變出乙個適當的rational來.於是它就那樣做了.換句話說此呼叫動作在編譯器眼中有點像這樣:

const rational temp(2);                        // 根據2建立乙個暫時性的rational物件

result = onehalf * temp; // 等同於onehalf.operator*(temp)

當然,只因為涉及non-explicit 建構函式,編譯器才會這樣做.如果rational建構函式是 explicit,一次啊語句沒有乙個可以通過編譯:

result = onehalf * 2;                        // error

result = 2 * onehalf; // error

這就很難讓rational class 支援混合式算數運算了,不過至少上述兩個句子的行為從此一致.

然而目標不僅在一致性,也要支援混合式算數運算,也就是希望有個設計能讓上述語句通過編譯.而之前的第乙個式子是正確的,而第二個式子是錯誤的.結論是,

只有當引數被列於引數列內,這個引數才是隱式型別轉換的合格參與者.地位相當於"被呼叫的成員函式所隸屬的那個物件"——即 this 物件——的那個隱喻引數,絕不是隱式轉換的合格參與者.這就是為什麼上述第一次呼叫可通過編譯,第二次呼叫則否,因為第一次呼叫伴隨乙個放在引數列內的引數,第二次呼叫則否.

然而一定也會想支援混合式算數運算.可行之道終於撥雲見日:讓 operator* 成為乙個non-member函式,以便允許編譯器在每乙個實參身上執行隱式型別轉換:

class rational ;

const rational operator* (const rational& lhs, const rational& rhs)

rational onefourth(1, 4);

rational result;

result = onefourth * 2; // ok

result = 2 * onefourth; // ok

這當然是個很好的結局,不過還有一點必須操心:operator* 是否應該成為rational class 的乙個 friend 函式呢?

就本例而言答案是否,因為 operator* 可以完全藉由rational的 public 介面完成任務.這匯出乙個重要的觀察:member函式的反面是non-member函式,不是 friend 函式.不能夠只因函式不應該成為member,就自動讓它成為 friend.

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

Effective C 條款8 第2章

prevent exception from leving destructors.c 並不禁止析構函式吐出異常,但它不鼓勵這樣做.這是有原因的,考慮以下 class widget 假設這個可能吐出乙個異常 void dosomething v在這裡被自動銷毀當vector v被銷毀,它有責任銷毀其...

Effective C 條款15 第3章

provide access to raw resources in resources managing classes 資源管理類 resource managing classes 很棒.它們是對抗資源洩露的堡壘.在乙個良好的環境中將依賴這樣的classes來處理和資源之間的所有互動.而不是直...

Effective C 條款23 第4章

prefer non member non friend functions to member functions 想象有個 class 用來表示網頁瀏覽器,這樣的 class 可能提供眾多函式,如下所示 class webbrowser 許多使用者會想一整個執行所有這些動作,因此webbrows...