Effective C++學(xué)習(xí)筆記(第六章)

條款32:確定public繼承塑模出is-a關(guān)系

is-ahas-a是C++類的兩個重要關(guān)系描述幢炸,如果類D基于public方式繼承于類B摹闽,則D類的實例 is-a B類的實例,適用于Base class身上的每一件事情也一定適用于Derived class身上箭启。

條款33:避免遮掩繼承而來的名稱

C++編譯器在編譯的時候針對類內(nèi)的成員函數(shù)也會按照變量域類似的規(guī)則進行搜索:即先在本類的作用域中搜索乾颁,如果找到對應(yīng)的符號涂乌,則不會繼續(xù)擴大搜索域,如果沒有找到則會擴大搜索域英岭,先擴展至其父類域湾盒,依次往上擴展∽缑茫考察下面的示例代碼:

#include <iostream>
class B {
public:
    void mf1() {}
    void mf2() {}
};
class D : public B {
public:
    void mf3() {}
    void mf1(int a) {}
};

int main()
{
    D obj;

    obj.mf3(); // 編譯OK罚勾,調(diào)用D::mf3()
    obj.mf1(2); // 編譯OK,調(diào)用D::mf1(int)
    obj.mf1(); // 編譯不通過
    obj.mf2(); // 編譯通過吭狡,調(diào)用B::mf2

    return 0;
}
  • 在編譯obj.mf3()時尖殃,直接就命中D類作用域定義的mf3函數(shù),并且參數(shù)與定義的一致划煮,編譯通過送丰。
  • 在編譯obj.mf1(2)時,因為在D類的作用域中有mf1函數(shù)弛秋,并且參數(shù)定義一致蚪战,編譯通過牵现。
  • 在編譯obj.mf1()時,因為D類作用域中有mf1函數(shù)邀桑,編譯器不會擴大搜索域了瞎疼,然后比較參數(shù)定義發(fā)現(xiàn)不一致,所以編譯不通過壁畸。
  • 在編譯obj.mf2()時贼急,由于D類作用域中沒有這個函數(shù)符號,所以編譯器擴大搜索域至其父類捏萍,即B類作用域太抓,在B類作用域中發(fā)現(xiàn)此函數(shù)符號,并且參數(shù)定義檢查一致令杈,所以編譯通過走敌。

結(jié)論:在子類中需要避免定義與父類中同名的函數(shù)(虛函數(shù)除外),因為那樣的話逗噩,子類對象實例就不能調(diào)用父類定義的該函數(shù)了掉丽,如果一定要調(diào)用,則需要在子類中使用using語句顯式的聲明父類被掩蓋的函數(shù)异雁,如下所示:

#include <iostream>
class B {
public:
    void mf1() {}
    void mf2() {}
};
class D : public B {
public:
    using B::mf1;  // 使用using語句聲明父類中被掩蓋的函數(shù)
    void mf3() {}
    void mf1(int a) {}
};

int main()
{
    D obj;

    obj.mf3(); // 編譯OK捶障,調(diào)用D::mf3()
    obj.mf1(2); // 編譯OK,調(diào)用D::mf1(int)
    obj.mf1(); // 現(xiàn)在編譯可以通過了
    obj.B::mf1(); // 等同于上面的寫法
    obj.mf2(); // 編譯通過纲刀,調(diào)用B::mf2

    return 0;
}
條款34:區(qū)分接口繼承和實現(xiàn)繼承
  • 純虛函數(shù)意味著接口繼承项炼,子類如果要實例化,則必須要實現(xiàn)父類中定義的純虛函數(shù)示绊。
  • 非純虛函數(shù)意味著接口繼承和繼承一份默認的實現(xiàn)锭部,該默認的實現(xiàn)在父類中給出(非純虛函數(shù)必須在父類函數(shù)中給出一份默認實現(xiàn)),子類可以給出自己的實現(xiàn)以覆蓋默認實現(xiàn)面褐。
  • 非虛函數(shù)意味著接口繼承以及強制性實現(xiàn)繼承拌禾,一般來說,如果在父類中定義了一個非虛函數(shù)盆耽,則子類中一般無需重新定義。
條款35:考慮virtual函數(shù)以外的其它選擇
  • 選擇1:NVI(Non-virtual Interface)手法扼菠,采用非虛函數(shù)調(diào)用虛函數(shù)的方式摄杂,如下:
#include <iostream>
class B {
public:
    void FuncWrapper() {std::cout << "Entering B::FooWrapper" << std::endl; Foo();}
private:
    virtual void Foo() {std::cout << "Entering B::Foo" << std::endl;};
};

class D : public B {
private:
    virtual void Foo() {std::cout << "Entering D::Foo" << std::endl;}
};

int main()
{
    D obj;
    obj.FuncWrapper();
    return 0;
}

在基類B中定義了一個非虛函數(shù)FuncWrapper,然后定義了一個一般虛函數(shù)Foo循榆,在FuncWrapper調(diào)用Foo析恢,這樣我們將Foo放在private域中,增強了類的封裝性秧饮。

  • 方法2:virtual函數(shù)替換為函數(shù)指針(或者std::function對象映挂,C++11之后的特性)泽篮,這是Strategy設(shè)計模式的具體形式。
#include <iostream>
#include <functional>
static void Func1()
{
    std::cout << "Calling Func1" << std::endl;
}
static void Func2()
{
    std::cout << "Calling Func2" << std::endl;
}

using MyFunc = std::function<void()>;  // 使用using語句代替?zhèn)鹘y(tǒng)的typedef柑船,定義一個函數(shù)類型
class MyClass {
public:
    MyClass(MyFunc func) : f(func) {}
    void DoSomething() {f();}
private:
    MyFunc f;
};

int main()
{
    MyClass obj1(Func1), obj2(Func2);

    obj1.DoSomething();
    obj2.DoSomething();

    return 0;
}

在這個MyClass類中帽撑,定義了一個私有成員變量,它代表處理函數(shù)真正的執(zhí)行動作鞍时,由于每個對象可能的執(zhí)行動作不一樣亏拉,所以可以在構(gòu)造函數(shù)中傳入具體的函數(shù),注意:這里使用了C++11之后的std::function模板逆巍,這是比傳統(tǒng)的函數(shù)指針更好用的一種C++方式及塘。

條款36:絕不重新定義繼承而來的非虛函數(shù)

一句話:子類切勿重新定義繼承自父類中的非虛函數(shù),如果你真的要這么做锐极,請將其定義成虛函數(shù)笙僚。

條款37:絕不重新定義繼承而來的缺省參數(shù)值

這是個C++考試中常考的一個知識點灵再,考察以下示例代碼:

#include <iostream>

class B {
public:
    virtual void Foo(int val = 1) {std::cout << "val = " << val << std::endl;}
};
class D : public B {
public:
    virtual void Foo(int val = 2) {std::cout << "val = " << val << std::endl;}
};

int main()
{
    B* p = new D();
    p->Foo();
    return 0;
}

可以看到在調(diào)用子類的Foo虛函數(shù)版本中肋层,參數(shù)卻是用了父類中的缺省值。
簡而言之:缺省參數(shù)的設(shè)置是靜態(tài)的檬嘀,而虛函數(shù)的執(zhí)行是動態(tài)的槽驶,所以請不要重新定義繼承而來的缺省值,否則會引起誤解鸳兽。

條款38:通過組合關(guān)系塑模出“has-a”
  • 一些C++設(shè)計模式中推薦盡可能使用組合而非繼承掂铐。
  • 組合意味著“has-a”關(guān)系。
條款39:明智而謹慎的使用private繼承
條款40:多重繼承

以上兩條不常用揍异,也不推薦使用全陨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市衷掷,隨后出現(xiàn)的幾起案子辱姨,更是在濱河造成了極大的恐慌,老刑警劉巖戚嗅,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雨涛,死亡現(xiàn)場離奇詭異,居然都是意外死亡懦胞,警方通過查閱死者的電腦和手機替久,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躏尉,“玉大人蚯根,你說我怎么就攤上這事≌兔樱” “怎么了颅拦?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵蒂誉,是天一觀的道長。 經(jīng)常有香客問我距帅,道長右锨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任锥债,我火速辦了婚禮陡蝇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哮肚。我一直安慰自己登夫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布允趟。 她就那樣靜靜地躺著恼策,像睡著了一般。 火紅的嫁衣襯著肌膚如雪潮剪。 梳的紋絲不亂的頭發(fā)上涣楷,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音抗碰,去河邊找鬼狮斗。 笑死,一個胖子當(dāng)著我的面吹牛弧蝇,可吹牛的內(nèi)容都是我干的碳褒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼看疗,長吁一口氣:“原來是場噩夢啊……” “哼沙峻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起两芳,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤摔寨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后怖辆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體是复,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年竖螃,在試婚紗的時候發(fā)現(xiàn)自己被綠了淑廊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡斑鼻,死狀恐怖蒋纬,靈堂內(nèi)的尸體忽然破棺而出猎荠,到底是詐尸還是另有隱情坚弱,我是刑警寧澤蜀备,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站荒叶,受9級特大地震影響碾阁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜些楣,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一脂凶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧愁茁,春花似錦蚕钦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至促煮,卻和暖如春邮屁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背菠齿。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工佑吝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绳匀。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓芋忿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親襟士。 傳聞我的和親對象是個殘疾皇子盗飒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容