學過C語言得人應該知道C++的三大特點:繼承,封裝,多態。
今天先來聊聊第一大特點 繼承。
一、繼承的概念
什麼叫繼承呢?就是面向對象程序設計代碼可以複用的一種機制,手段。並且可以在保持原有類的特性上進行擴展,增加功能,這樣產生得新的類,就叫做派生類。
通俗的說派生類就是子類,兒它所繼承的基類又叫做父類。
二、繼承關係
(1)由繼承的定義格式可知,繼承有三大類:public(公有繼承)、protected(保護繼承)、private(私有繼承)。
代碼舉例:
class Base
{
public:
int pub;
protected:
int pro;
private:
int pri;
};
class Derived :public/*protected*//*private*/Base
{
public:
int _pub;
};
但需要注意的是,基類的private成員在派生類中是不能被訪問的。
繼承關係的圖例:
(2)繼承的小總結
1>基類的private成員在派生類中是不能被訪問的.
2>public繼承是一個接口繼承,保持is-a原則,每個父類可用的成員對子類也可用,因爲每個子類對象也都是一個父類對象。
3>protected/private繼承是一個實現繼承,基類的部分成員並非完全成爲子類接口的一部分,是 has-a 的關係原則,所以非特殊情況下不會使用這兩種繼承關係,在絕大多數的
場景下使用的都是公有繼承。
4>不管是哪種繼承方式,在派生類內部都可以訪問基類的公有成員和保護成員,基類的私有成員存在但是在子類中不可見(不能訪問)。
5>使用關鍵字class時默認的繼承方式是private,使用struct時默認的繼承方式是public,不過最好顯示的寫出繼承方式。
三、派生類
1)繼承關係中構造函數調用順序
需要說明的是:
1>基類沒有缺省構造函數,派生類必須要在初始化列表中顯式給出基類名和參數列表。(基類中沒有,派生類中必須給出)
2>基類沒有定義(帶參數/無缺省值)構造函數,則派生類也可以不用(可定義也可不定義)定義,全部使用缺省構造函數。
3>基類定義了帶有形參表構造函數,派生類就一定定義構造函數。
而析構函數調用過程正好與構造函數相反。
下面用簡單例子來證明:
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "基類構造" << endl;
}
~Base()
{
cout << "基類析構" << endl;
}
int pub;
protected:
int pro;
private:
int pri;
};
class Derived :public Base
{
public:
Derived()
:Base()
, d_pub()
{
cout << "派生類構造" << endl;
}
~Derived()
{
cout << "派生類析構" << endl;
}
private:
int d_pub;
};
void test()
{
Derived d;
}
int main()
{
test();
return 0;
}
運行結果如下:
四、繼承體系中的作用域
1)在繼承體系中,基類和派生類是兩個不同作用域。
2)子類和父類有同名成員時,通過派生類對象訪問時優先訪問派生類中的成員,要想訪問基類中成員必須加作用域限定符。
同名隱藏:在繼承體系中,如果基類和派生類有同名成員,如果使用派生類對象調用基類和派生類的同名成員,優先調用派生類同名成員。
函數重載:相同作用域,函數名相同,參數列表不同。
五、繼承與轉換——賦值兼容規則
1)繼承模型
1>單繼承:一個子類只有一個父類。
2>多繼承:一個子類有多個父類。
class B1
{
public:
int _b1;
};
class B2
{
public:
int _b2;
};
//單繼承
class A :public B1
{
public:
int _a;
};
//多繼承
class D :public B1,public B2
{
public:
int _d;
};
void test()
{
A a;
D d;
}
int main()
{
test();
return 0;
}
單繼承和多繼承的繼承模型都爲:基類在上,派生類在下3>菱形繼承
2)繼承的二義性(虛繼承)
代碼如下:
class B
{
public:
int _b;
};
class C1:public B
{
public:
int _c1;
};
class C2:public B
{
public:
int _c2;
};
class D :public C1,public C2
{
public:
int _d;
};
int main()
{
D d;
d._b;
return 0;
}
要解決二義性,這就要談到虛繼承,即在公有繼承錢加上關鍵字virtual就解決了
class B
{
public:
B() :_b(0)
{
}
int _b;
};
class C1 :virtual public B
{
public:
C1() :_c1(1)
{
}
int _c1;
};
class C2 :virtual public B
{
public:
C2() :_c2(2)
{
}
int _c2;
};
class D :public C1,public C2
{
public:
D() :B(),_d(3)
{
}
int _d;
};
int main()
{
D d;
d._b;
return 0;
}
運行後,通過監視窗口觀察:
簡單的來說就是: 偏移量表格
3)友元關係不能繼承。
六、虛擬繼承與普通繼承的區別
1)書寫形式——虛擬關鍵字
2)對象模型——多了4個字節—地址——偏移量表格
普通繼承:基類部分在前,普通類在後
虛擬繼承:基類部分在最底下
3)對於繼承成員訪問形式
普通繼承:直接訪問
虛擬繼承:偏移量表格——相對於基類對象的偏移量
4)訪問基類成員——構造函數不同:1>派生類----合成構造函數-----將偏移量表格地址存放在對象前四個字節
2>多一個參數--1—檢測是否爲虛擬繼承
好了,以上就是我對繼承的一點簡單總結,請大神們多多指教。