C++:類的私有繼承、公有繼承、多重繼承討論
背景
第十四章課後練習第一題中,再次提及了私有繼承、公有繼承和多重繼承。想了想好像這塊兒的概念有點淡忘,感覺比較重要,特意寫個小結。果然C++的越來越多的關鍵字很被人厭煩是有道理的啊,不經常使用的話好容易忘記。
公有繼承
/*抽象基類*/ class worker { protected: virtual void data() const; virtual void get(); private: string fullname; long id; public: worker() : fullname("none"), id(0) {}; worker(const string& fn, long n) : fullname(fn), id(n) {}; virtual ~worker() {}; virtual void set() = 0;/*抽象基類定義為純需函式,不進行例項化 */ virtual void show() const = 0; }; /*worker派生類*/ class waiter :public virtual worker//虛基類,這樣singer就只繼承了一個worker { protected: virtual void data() const; virtual void get(); private: int panache; public: waiter() : worker(), panache() {}; waiter(const string& fn, long n, int p) : worker(fn,n), panache(p) {}; waiter(const worker& wk, int p = 0) :worker(wk), panache(p) {}; virtual ~waiter() {}; virtual void set();/*繼承來的,不定義virtual也行*/ virtual void show() const;/*繼承來的,不定義virtual也行*/ };
這串程式碼中就體現了公有繼承public,當然繼承的物件是一個虛基類(虛基類和非虛基類的區別就是在mian中定義派生類否會產生多個基類物件的區別)。繼續談公有繼承,公有繼承顧名思義就是繼承基類的公有介面,比如上述程式碼中water
派生類會繼承set()
和show()
等public:
宣告中的其他介面,因此獲得了實現,也獲得了介面。還有比較有意思的一點是,一個基類支援派生出多個公有派生類,但是需要宣告為虛(原因已經說明)。
私有繼承
/*私有繼承+多重繼承*/ class student : private string,private valarray<double> { private: typedef valarray <double> ArrayDB; //放到類裡面,為了限制ArrayDB不能被外部呼叫 string name;//string類過來的,也就是多重繼承,name也可以用string類的方法 ArrayDB scores;//valarray類,多重繼承,scores也可以用valarray類的方法 /*新增一個介面用來呼叫私有基類的size方法*/ ostream &arr_out(ostream & os) const; public: /*此時string是一個未命名類,需要呼叫string類的建構函式進行初始化*/ student() : string("none student")/*呼叫string的建構函式*/, ArrayDB()/*呼叫valarray的建構函式*/ {};//初始化列表 /*傳遞一個整數的定義array有n各物件的*/ student(const string& s, int n) :string(s), ArrayDB(n) {}; /*傳遞一個valarray的*/ student(const string& s, const ArrayDB& a) :string(s), ArrayDB(a) {}; student(const string& s, const double* pd, int n) :string(s), ArrayDB(pd/*成員定義*/, n/*成員數量*/) {}; ~student() {}; /*方法*/ double average() const;//統計平均成績 const string& Name() const;//返回name double& operator[](const int n/*陣列的編號*/);//操作成員,可以修改 friend istream& operator>>(istream& is, student& st); friend ostream& operator<<(ostream& os, student& st); friend istream& getline(istream& is, student& st); /*顯式轉換 將string類物件轉為student類物件 同時初始化其他引數*/ explicit student(const string& s) : string(s), ArrayDB() {}; /*顯式轉換 */ explicit student(const int n) : string("none student"), ArrayDB(n) {}; };
私有繼承相對來說更私密一些,怎末說呢?就是私有繼承會將基類的公有方法變為派生類的私有方法,也就是說派生類定義一個類物件後,想在類物件中訪問私有基類的公有介面,必須使用派生類的共有介面(此介面包含了私有基類的方法並且是通過作用域解析運算子::
和強制型別轉換(cosnt string) student
來實現),不能夠直接像公有繼承一樣直接在定義一個物件後直接訪問私有基類的公有介面了。所以私有繼承其實也能通過某種手段去訪問基類的介面,但是這就違背了私有繼承的本意了。總之,私有繼承也就是獲得實現,但不獲得介面,這一點和包含關係一樣。
多重繼承
吶~一個派生類繼承了多個基類方法的現象叫做多重繼承。woker是虛基類,singer和waiter是虛基類的派生類,採用公有繼承。而singerwaiter是將singer和waiter作為基類的派生類,也就是多重繼承。
總結
- 包含:獲得實現,也獲得介面,可以是多種類物件
- 私有繼承:獲得實現、不獲得介面;但是隻能是一種類物件
- 公有繼承:獲得實現、也獲得介面
- 多重繼承:獲得所有基類的實現,但不一定獲得介面
回到背景原因,我們什麼時候使用私有繼承、什麼時候使用公有繼承呢?就本白現在的理解,當基類的方法和物件全部都使用於派生類時,使用公有繼承,這樣隨便訪問都不會出現訪問錯誤的現象;當基類的方法和物件並不完全適用於派生類時,應考慮私有繼承,因為你不想讓派生類訪問一些基類的不適用成員。