在C++的類中有許多種繼承方式,而我們在軟件設計和編寫代碼時用得最多的就是public繼承,我們很少接觸到private繼承。但是我們在設計時真的有思考過什麼時候應該用public繼承,什麼時候不該使用public繼承,什麼時候應該想想那些經常被我們遺忘的知識,讓我們從它們被遺忘的角落裏重拾它的光芒。例如private繼承。
#include <iostream> using namespace std; class Base { public: void Bfun1(){cout << "I'm Bfun1()" << endl;} protected: void Bfun2(){cout << "I'm Bfun2()" << endl;} private: void Bfun3(){cout << "I'm Bfun3()" << endl;} }; class Derived : private Base { public: void Dfun1() {cout << "I'm Dfun1() " ;Bfun1();} void Dfun2() {cout << "I'm Dfun2() " ;Bfun2();} }; int main() { Derived d; d.Dfun1(); d.Dfun2(); //d.Bfun1(); //d.Bfun2(); // d.Bfun3(); return 0; }
運行後我們可以發現:1)派生類對象d是無法訪問基類的任一成員函數的(包括public成員函數),因爲這是private繼承
2)派生類本身是能訪問基類的public 和protected成員的,如Dfun1函數訪問了Bfun1 Dfun2函數訪問了Bfun2;
二、關於《Effective c++》中Item 39
1、如果類之間的關係是private繼承,則:a:編譯器不會自動將一個派生類對象轉換成一個基類對象 b: 基類中的所有成員在派生類中都編程private屬性,派生類對象無法訪問
2、private表現出的關係是“根據某物實現出”,它繼承的是實現而非接口
乍看很奇怪,實現都繼承了怎麼接口都不繼承。這樣理解,我們派生類需要利用private 基類中的一些成員實現某些東西,但是成員經過繼承後變成了private屬性,用戶是無法通過接口訪問它的,所以只是繼承了實現而忽略了接口。
3、private繼承主要用於:當兩個類不存在“is-a”的關係,其中一個需要訪問另一個的protected成員,或需要重新定義一個或多個virtual函數
這句話這麼理解:現有兩個類並非“is-a”關係,類D需要用類B的一些成員去實現它,而且我要用到的這些成員往往是類B的protected成員或者是虛函數。但是我只是需要這些成員去實現我的類D,並不給用戶提供接口能直接訪問類B的這些成員,這時候就應該用private繼承。
我們看一個例子吧,這是Item 40改編過來的。
我希望實現一個類CPerson 它能提取人的name 和 birthdate,我想偷個懶,發現有這麼一個類PersonInfo能實現差不多的功能,只是Person的格式有點不符合我的要求,我就打算利用PersonInfo來實現我Cperson的name函數和birthdate函數。
class PersonInfo{
public:
explicit PersonInfo(DatabaseID pid);
virtual ~PersonInfo();
virtual const char* theName() const;
virtual const char* theBirthDate() const;
...
private:
virtual const char* valueDelimOpen() const{return "["}; //“開始”符號,用於姓名的輸出
virtual const char* valueDelimClose() const{return "]"}; //“結束”符號
...
};
const char* PersonInfo::theName() const{
static char value[Max_Formatted_Field_Value_Length]; //保留緩衝區給返回值使用。注意由於緩衝區是static的,會被自動初始化爲“全部是0”
std::strcpy(value, valueDelimOpen()); //寫入起始符號
... //添加姓名
std::strcat(value, valueDelimClose()); //寫入結束符號
return value;
}
class CPerson : private PersonInfo{
public:
std::string name()const
{return PersonInfo::theName();}
std::string birthDate()const
{return PersonInfo::theBirthDate();}
private:
virtual const char* valueDelimOpen() const{return ""};
virtual const char* valueDelimClose() const{return ""};
...
};
我們發現:a:類CPerson的name通過調用類PersonInfo的成員函數實現,我這裏用了它的實現,卻沒有給用戶提供調用類PersonInfo成員的接口(因爲是private繼承)
b:由於PersonInfo的格式不符合我的要求,(它是用的方括號[ ],我卻不想要方括號),而控制格式的是兩個虛函數valueDelimOpen valueDelimClose 這裏我就可以重定義virtual函數了,在Cperson中,我們重定義了上述兩個控制格式的函數。 這就是上面說的“需要重新定義一個或多個virtual函數”的情況