**:
繼承
: 類的繼承,就是新的類從已有類那裡得到已有的特性。原有的類稱為基類或父類,產生的新類稱為派生類或子類。
派生類的宣告:
class 派生類名:繼承方式 基類名1, 繼承方式 基類名2,...,繼承方式 基類名n
;
在 c++ 中,乙個派生類可以同時有多個基類,這種情況稱為多重繼承。如果派生類只有乙個基類,稱為單繼承。派生類繼承基類中除構造和析構函式以外的所有成員。
繼承方式規定了如何訪問基類繼承的成員。繼承方式有public, private, protected。如果不顯示給出繼承方式,預設為private繼承。繼承方式指定了派生類成員以及類外物件對於從基類繼承來的成員的訪問許可權。
派生類中由基類繼承而來的成員的初始化工作還是由基類的建構函式完成,派生類中新增的成員在派生類的建構函式中初始化。
派生類建構函式的語法:
cpp?linenums 派生類名::派生類名(引數總表):基類名1(參數列1),基類名(引數名2)....基類名n(引數名n),內嵌子物件1(參數列1),內嵌子物件2(參數列2)....內嵌子物件n(參數列n)
注:建構函式的初始化順序並不以上面的順序進行,而是根據宣告的順序初始化。
如果基類中沒有不帶引數的建構函式,那麼在派生類的建構函式中必須呼叫基類建構函式,以初始化基類成員。
派生類建構函式執行的次序:
呼叫基類建構函式,呼叫順序按照它們被繼承時宣告的順序(從左到右);
呼叫內嵌成員物件的建構函式,呼叫順序按照它們在類中宣告的順序;
派生類的建構函式體中的內容。
派生類的析構函式的功能是在該物件消亡之前進行一些必要的清理工作,析構函式沒有型別,也沒有引數。析構函式的執行順序與建構函式相反。
例項:
#include #include using namespace std;
// 基類 b1
class b1
private:
b1 memberb1;
b2 memberb2;
b3 memberb3;
};int main()
/* 輸出結果 */
/*constructing b2 2
constructing b1 1
constructing b3
constructing b1 3
constructing b2 4
constructing b3
destructing b3
destructing b2
destructing b1
destructing b3
destructing b1
destructing b2
*/
在單繼承下,基類的public 和protected 成員可以直接被訪問,就像它們是派生類的成員一樣,對多繼承這也是正確的。但是在多繼承下,派生類可以從兩個或者更多個基類中繼承同名的成員。然而在這種情況下,直接訪問是二義的,將導致編譯時刻錯誤。
示例:
#include using namespace std;
class a
;class b
;class c : public a, public b
;int main()
使用成員名限定法可以消除二義性,但是更好的解決辦法是在類c中定義乙個同名函式 f(), 類c中的 f() 再根據需要來決定呼叫a::f()
orb::f()
, 這樣c1.f()
將呼叫c::f()
.
當乙個派生類從多個基類派生類,而這些基類又有乙個共同的基類,則對該基類中說明的成員進行訪問時,也可能會出現二義性。
示例:
// 派生類 b1,b2 繼承相同的基類 a, 派生類 c 繼承 b1, b2
class a
;class b1 : public a
;class b2 : public a
;class c : public b1, public b2
;int main()
c1.a;
c1.a::a;
這兩個訪問都有二義性,c1.b1::a;
c1.b2::a;
是正確的:
類c的成員函式f()
用如下定義可以消除二義性:
int c::f()
由於二義性的原因,乙個類不可以從同乙個類中直接繼承一次以上。
多繼承時很容易產生命名衝突,即使我們很小心地將所有類中的成員變數和成員函式都命名為不同的名字,命名衝突依然有可能發生,比如非常經典的菱形繼承層次。如下圖所示:
graph td;
a-->b;
a-->c;
b-->d;
c-->d;
類a派生出類b和類c,類d繼承自類b和類c,這個時候類a中的成員變數和成員函式繼承到類d中變成了兩份,乙份來自 a-->b-->d 這一路,另乙份來自 a-->c-->d 這一條路。當d訪問從a中繼承的資料時,變一起將無法決定採用哪一條路傳過來的資料,於是便出現了虛基類。
在乙個派生類中保留間接基類的多份同名成員,雖然可以在不同的成員變數中分別存放不同的資料,但大多數情況下這是多餘的:因為保留多份成員變數不僅占用較多的儲存空間,還容易產生命名衝突,而且很少有這樣的需求。使用虛基類,可以使得在派生類中只保留間接基類的乙份成員。
宣告虛基類只需要在繼承方式前面加上 virtual 關鍵字,如下面示例:
#include using namespace std;
class a
};class b: virtual public a
};class c: virtual public a
};class d: virtual public b, virtual public c
void display();
};void d::display()
/* 執行結果:
a=1b=2
c=3d=4
*/
本例中我們使用了虛基類,在派生類d中只有乙份成員變數 a 的拷貝,所以在 display() 函式中可以直接訪問 a,而不用加類名和域解析符。
虛基類的初始化
:請注意派生類d的建構函式,與以往的用法有所不同。以往,在派生類的建構函式中只需負責對其直接基類初始化,再由其直接基類負責對間接基類初始化。現在,由於虛基類在派生類中只有乙份成員變數,所以對這份成員變數的初始化必須由派生類直接給出。如果不由最後的派生類直接對虛基類初始化,而由虛基類的直接派生類(如類b和類c)對虛基類初始化,就有可能由於在類b和類c的建構函式中對虛基類給出不同的初始化引數而產生矛盾。所以規定:在最後的派生類中不僅要負責對其直接基類進行初始化,還要負責對虛基類初始化。
在上述**中,類d的建構函式通過初始化表調了虛基類的建構函式a,而類b和類c的建構函式也通過初始化表呼叫了虛基類的建構函式a,這樣虛基類的建構函式豈非被呼叫了3次?大家不必過慮,c++編譯系統只執行最後的派生類對虛基類的建構函式的呼叫,而忽略虛基類的其他派生類(如類b和類c)對虛基類的建構函式的呼叫,這就保證了虛基類的資料成員不會被多次初始化。
最後請注意:為了保證虛基類在派生類中只繼承一次,應當在該基類的所有直接派生類中宣告為虛基類,否則仍然會出現對基類的多次繼承。
賦值相容
: 賦值相容規則是指在需要基類物件的任何地方都可以使用公有派生類的物件來替代。
賦值相容規則中所指的替代包括:
c 三大特性之 繼承
繼承 乙個非常自然的概念,現實中的很多事情都是具有繼承性的。類似於自己繼承父母的特性,這也是繼承的特性 而繼承的上層稱為基類,下一層就叫做派生類。格式 class 派生類 繼承方式 基類 繼承 例如 include using namespace std class person class stu...
C 三大特性之繼承簡述
一 概念 派生類,基類 protected的成員,只能在成員方法中訪問 預設繼承方式為private 二 例項化方式 1 堆中例項化物件 worker p new worker delete p delete手動釋放記憶體,否則記憶體洩漏 p null 將指標至於安全狀態 2 棧中例項化物件 wor...
C 三大特性 封裝,繼承,多型
c 三大特性 封裝,繼承,多型 封裝 定義 封裝就是將抽象得到的資料和行為相結合,形成乙個有機的整體,也就是將資料與運算元據的源 進行有機的結合,形成類,其中資料和函式都是類的成員,目的在於將物件的使用者和設計者分開,以提高軟體的可維護性和可修改性 特性 1.結合性,即是將屬性和方法結合 2.資訊隱...