Item 39 明智地使用 private 繼承

2022-05-28 04:33:10 字數 2111 閱讀 1897

public 繼承表示 "is-a" 的關係,這是因為編譯器會在需要的時候將子類物件隱式轉換為父類物件。 然而 private 繼承則不然:

class person ;

class student: private person ;

void eat(const person& p);

person p;

student s;

eat(p); // fine, p is a person

eat(s); // error! a student isn't a person

person 可以 eat,但 student 卻不能 eat。這是 private 繼承和 public 繼承的不同之處:

子類繼承了父類的實現,而沒有繼承任何介面(因為public成員都變成 private 了)。 因此 private 繼承是軟體實現中的概念,與軟體設計無關。 private 繼承和物件組合類似,都可以表示 "is-implemented-in-terms-with" 的關係。在物件導向設計中,物件組合往往比繼承提供更大的靈活性,只要可以使用物件組合就不要用 private 繼承。

我們的 widget 類需要執行週期性任務,於是希望繼承 timer 的實現。 因為 widget 不是乙個 timer,所以我們選擇了 private 繼承:

class timer ;

class widget: private timer ;

在 widget 中重寫虛函式 ontick,使得 widget 可以周期性地執行某個任務。為什麼 widget 要把 ontick 宣告為private 呢? 因為 ontick 只是 widget 的內部實現而非公共介面,我們不希望客戶呼叫它。

private 繼承的實現非常簡單,而且有時只能使用 private 繼承:

我們知道物件組合也可以表達 "is-implemented-in-terms-of" 的關係, 上面的需求當然也可以使用物件組合的方式實現。但由於需要重寫 timer 的虛函式,所以還是需要乙個繼承關係的:

class widget ;

widgettimer timer;

};

內部類 widgettimer public 繼承自 timer,然後在 widget 中儲存乙個 widget timer物件。 這是 public 繼承+物件組合的方式,比 private 繼承略為複雜。但物件組合仍然擁有它的好處:

我們講雖然物件組合優於 private 繼承,但有些特殊情況下仍然可以選擇 private 繼承。 需要ebo(empty base optimization)的場景便是另乙個特例。 由於技術原因,c++中的獨立空物件也必須擁有非零的大小:

class empty {}; 

class holdsanint ;

empty e 是乙個空物件,但你會發現 sizeof(holdsanint) > sizeof(int)。 因為 c++ 中獨立空物件必須有非零大小,所以編譯器會在 empty 裡面插入乙個char,這樣 empty 大小就是1。 由於位元組對齊的原因,在多數編譯器中holdsanint 的大小通常為2*sizeof(int)。 但如果你繼承了empty,情況便會不同:

class holdsanint: private empty ;
這時 sizeof(holdsanint) == sizeof(int),這就是空基類優化(empty base optimization,ebo)。 當你需要ebo來減小物件大小時,可以使用private繼承的方式。

繼承乙個空物件有什麼用呢?雖然空物件不可以有非靜態成員,但它可以包含 typedef, enum, 靜態成員,非虛函式 (因為虛函式的存在會導致乙個虛函式指標,它將不再是空物件)。 stl就定義了很多有用的空物件,比如unary_function, binary_function 等。

明智地使用Pimpl

明智地使用pimpl 首先引用一下別人的內容 pimpl 用法背後的思想是把客戶與所有關於類的私有部分的知識隔離開。由於客戶是依賴於類的標頭檔案的,標頭檔案中的任何變化都會影響客戶,即使僅是對私有節或保護節的修改。pimpl用法隱藏了這些細節,方法是將私有資料和函式放入乙個單獨的類中,並儲存在乙個實...

條款42 明智地使用私有繼承

條款35說明,c 將公有繼承視為 是乙個 的關係。它是通過這個例子來證實的 假如某個類層次結構中,student類從person類公有繼承,為了使某個函式成功呼叫,編譯器可以在必要時隱式地將student轉換為person。這個例子很值得再看一遍,只是現在,公有繼承換成了私有繼承 class per...

條款42 明智地使用私有繼承

第乙個規則是,和公有繼承相反,如果兩個類之間的繼承關係為私有,編譯器一般不會將派生類物件 如student 轉換成基類物件 如person 第二個規則是,從私有基類繼承而來的成員都成為了派生類的私有成員,即使它們在基類中是保護或公有成員,即派生類物件不能訪問基類的所有成員 class person ...