條款36:絕不重新定義繼承而來的non-virtual函數
? ? ? ? ? non-virtual函數執行的是靜態綁定,在編譯器就已經決定,因此對象對用的函數只和指針的類型有關,而與指針所指的對象無關;記住non-virtual函數的性質:不變性凌駕于特異性;
條款37:絕不重新定義繼承而來的缺省參數值
? ? ? ? ?靜態類型是指在程序中聲明時使用的類型,動態類型是指目前所指對象的類型,動態類型變現為一個對象將會有什么樣的行為;
? ? ? non-virtual和參數缺省值執行的是靜態綁定,virtual執行的是動態綁定,代碼分析:
class shape{
public:
? ?enum shapecolor { red, green, blue };
? virtual void draw(shapecolor color=red) const=0;
};
class rectangle:public shape{
public:
? ? ? ? ? virtual void draw(shapecolor color=green) const;//糟糕的操作
? ? ? ? ?...
};
class circle:public shape{
public:
? ? ? ? ? ? ? virtual void draw(shapecolor color)const;
? ? ? ? ? ? ? ...
};
調用 shape *pr=new rectangle;
? ? ? ? pr->draw( );//執行的代碼為rectangle::draw(shape::red):
? ? ? ? ? ? ? ? ? ? ? ? ? //相當于shape執行缺省,rectangle執行virtual函數,一人一半,神奇的操作;
C++這樣設計的原因:運行期效率,如果缺省值是動態綁定,編譯器就必須用某種方法在運行期為virtual函數決定適當的參數缺省值,這比目前實現的在編譯期決定的機制更慢更加復雜;
條款38:通過復合塑模出has-a或者根據某物實現出
? ? ? ? ? 復合是類型之間的一種關系,當某種類型的對象內含它種類型的對象,就是這種關系;復合有兩種意義,復合意味著has-a(有一個)或is-implemented-in-terms-of(根據某物實現出);
? ? ? ? ?如何區分is-a(是一種)和is-implemented-in-terms-of(根據某物實現出)這兩種關系,可以通過public繼承中D繼承B,D對象也是B對象,反之B對象不是D對象來判斷;
條款39:明智而審慎地使用private繼承
? ? ? ? ? private繼承意味著implemented-in-terms-of(復合),它只有實現部分被繼承,接口部分省略;private繼承純粹是一種實現技術,它在軟件設計層面上沒有意義,其意義只及于軟件實現層面;代碼分析:
? ? ? class person {...};
? ? ? class student:private person{ . . .};
? ? ? void eat(const person&p);
? ? ? person p;??? student s;? ??? eat(p);
? ? ? eat(s);//編譯不通過
原因:1)private繼承中編譯器不會自動將一個derived class對象轉換為一個base class對象;2)private繼承而來的所有成員在derived? class中都會變成private屬性,縱使它們在base class中原本是public或者protected;? ?
? ? ? ?盡可能的多用復合,必要的時候才使用private繼承(protected成員和virtual函數牽涉進來的時候,或者繼承一個empty class時候可以采用private繼承)
? ? ? ?需求:定義一個類B(繼承一個類B_B),使得這個類的派生D不能調用B_B的成員函數;
設計1:private繼承
class B_B{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?class D:private B_B{
public:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?private:?
? ? ? ?explicit B_B(int tickfrency);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? virtual void ontick( ) const;
? ? ? ?virtual void ontick( )const;
};? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? };
設計2:繼承+復合
class D{
private:
? ? ? ? class widgettimer:public B_B{
? ? ? ? ?public:? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?virtual void ontick( )const;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ...
? ? ? ? };
? ? widgwttimer timer;
}
在D內聲明一個嵌套式private class,后者以public繼承B_B并重新定義ontick,然后放這個類型在D對象內;
選擇方案2不選擇方案1的原因:1)想阻止D的derived class重新定義ontick函數;2)降低D編譯的依存性;
? ? ? ? ? empty class:沒有non_static成員變量,沒有virtual函數,沒有virtual base class;empty class數據大小并不是零,C++規定凡是獨立對象都必須有非零大小(這個約束不針對derived class 中的base class成分,它們是非獨立的),因此一個empty class 大小為1(C++規定安插一個char類型到空對象中);
? ? ? ? ?在繼承empty class中,如果選擇private繼承(EBO,empty base optimization,繼承空類),可能造成empty base的最優化;