一.組合
1.定義:
組合就是一個類的對象具備了某一個屬性,該屬性的值是指向另一個類的對象
2.用處:
解決類與類之間代碼冗餘的問題
二.繼承
1.定義
用一個數據類型來定義一個新的數據類型,定義的新類型(派生類或子類)既有原來數據(基類或父類)中的成員,也能添加自己的成員。
2.分類
1.單繼承
定義的格式
class 派生類名:繼承方式 基類名
{
派生類成員
};
2.多繼承
定義的格式
class 派生類名:繼承方式 基類名,繼承方式 基類名
{
派生類成員;
};
在多繼承中靠近派生類的基類屬於先聲明的,數據在派生類中先保存。
3.菱形繼承
當一個子進程繼承了多個父類的時候,多個父類最終繼承了同一個類,這種繼承稱之爲菱形繼承
4.虛繼承
class Furniture{……};
class Bed : virtual public Furniture{……}; // 使用虛繼承
class Sofa : virtual public Furniture{……};// 使用虛繼承
class sleepSofa : public Bed, public Sofa {……};
//Furniture類只需要構造一次
3.三種繼承方式
public 公有繼承
private 私有繼承
protected 保護繼承
訪問方式 | 類裏面 | 類外面 |
---|---|---|
public | 允許訪問 | 允許訪問 |
protected | 允許訪問 | 不允許訪問 |
private | 允許訪問 | 不允許訪問 |
繼承方式 | 基類的public成員 | 基類的protected成員 | 基類的private成員 | 繼承引起的訪問控制關係變化概括 |
---|---|---|---|---|
public繼承 | 仍爲public成員 | 仍爲protected成員 | 不可見 | 基類的非私有長遠在子類的訪問屬性不變 |
protected繼承 | 變爲protected成員 | 變爲protected成員 | 不可見 | 基類的非私有成員都爲子類的保護成員 |
private繼承 | 變爲private成員 | 變爲private成員 | 不可見 | 基類中的非私有成員都稱爲子類的私有成員 |
class默認私有繼承,struct默認公有繼承
4.友元函數的繼承
1.友元函數和友元類
友元函數:可以訪問指定類的私有和受保護的自定義成員,如果不是被指定的成員,則不能被訪問。
友元類:友元關係是單向的,也不能被傳遞。
2.注意事項
1.友元函數可以訪問類的私有成員,但不是類的成員函數
2.友元函數不能用const修飾
3.友元函數可以在類定義的任何地方聲明,不受類訪問限定符的限定
4.一個函數可以是多個類的友元函數
5.友元關係不能繼承
5.靜態成員的繼承
靜態成員可以繼承,一個繼承體系中static成員只有一個,無論有多少個派生類,都僅僅有一個static成員
三.多態
1.定義
指同一個事物的多種狀態
2.用處
多態性:繼承同一個類的多個子類中有相同的方法名,這時子類產生的對象就可以不用考慮具體的類型而直接調用該功能
3.靜態多態性
1.函數重載和缺省參數
1.重載的函數必須有相同的函數名
2.重載的函數不可以擁有相同的參數
只有在 同一類定義中的同名成員函數才存在重載關係 ,主要特點是 函數的參數類型和數目有所不同 ,但 不能出現函數參數的個數和類型均相同 ,僅僅依靠返回值類型不同來區分的函數,這和普通函數的重載是完全一致的。另外,重載和成員函數是否是虛函數無關
3.默認參數只有卸載函數聲明中即可
4.默認參數應該儘量靠近函數參數列表的最右邊,以防二義性。
比如
double sum (float nNum2 = 10,float nNum1);
這樣的函數聲明,我們調用時:sum(15);程序就有可能無法匹配正確的函數而出現編譯錯誤。
2.運算符重載
用法
返回值 operator 運算符(參數列表)
{
//code
}
3.宏多態
帶變量的宏可以實現一種初級形式的靜態多態
4.動態多態性
1.虛表和虛函數
#include<iostream>
using namespace std;
class animal
{
public:
void sleep(){
cout<<"animal sleep"<<endl;
}
virtual void breathe(){
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe(){
cout<<"fish bubble"<<endl;
}
};
int main()
{
fish fh;
animal *pAnimal=&fh;
pAnimal->breathe();
}
編譯器爲每個類的對象提供一個虛表指針,這個指針指向對象所屬類的虛表。
虛表指針在構造函數中進行虛表的創建和虛標指針的初始化。
對於虛函數調用來說,每一個對象內部都有一個虛表指針,該虛表指針被初始化爲本類的虛表。所以在程序中,不管你的對象類型如何轉換,但該對象內部的虛表指針是固定的,所以呢,才能實現動態的對象函數調用,這就是C++多態性實現的原理。
總結(基類有虛函數):
1、每一個類都有虛表。
2、虛表可以繼承。如果基類3個虛函數,那麼基類的虛表中就有三項(虛函數地址),派生類也會有虛表,至少有三項,如果重寫了相應的虛函數,那麼虛表中的地址就會改變,指向自身的虛函數實現。如果派生類有自己的虛函數,那麼虛表中就會添加該項。
3、派生類的虛表中虛函數地址的排列順序和基類的虛表中虛函數地址排列順序相同。
2.虛函數和純虛函數
1.引入原因
虛函數:爲了方便使用多態,在基類中定義虛函數
純虛函數:爲了實現多態性,自己不實現過程,讓繼承他的子類實現(抽象類即包含純虛函數的類)
純虛函數在基類只定義函數體,沒有實現過程
virtual void Eat()=0;直接=0
2.區別
(1)虛函數中的函數是實現的,哪怕是空實現,它的作用是這個函數在子類裏面可以被重載,運行時動態綁定實現動態
純虛函數是個接口,是個函數聲明,在基類中不實現,要等到子類中去實現
(2) 虛函數在子類裏可以不重載,但是虛函數必須在子類裏去實現。
四.模板
1.定義
template<typename T>.//在模板定義語法中關鍵字class與typename的作用完全一樣
函數模板必須由編譯器根據程序員的調用類型實例化爲可執行的函數
2.類模板和模板類
1.類模板
一個類模板(類生成類)允許用戶爲類定義個一種模式,使得類中的某些數據成員、默認成員函數的參數,某些成員函數的返回值,能夠取任意類型(包括系統預定義的和用戶自定義的)。
一個類中的數據成員的數據類型不能確定,或者是某個成員函數的參數或返回值的類型不能確定,就必須將此類聲明爲模板,它的存在不是代表一個具體的、實際的類,而是代表一類類。
類模板的使用即爲將一個類模板實例化爲一個具體的類,格式:類名<實際的類型>
一個類定義中,只要有一個函數模板,則這個類是類模板;類模板的成員函數不全是是函數模板,當類模板實例化後,成員函數也隨之實例化。
2.模板類
類模板實例化後的產物
五.覆蓋
1.在派生類中覆蓋基類中的同名函數,要求兩個函數的參數個數、參數類型、返回類型都相同。
2.基類函數必須是虛函數
六.隱藏
1.定義
派生類中的函數屏蔽了基類中的同名函數
2.注意
1.2個函數名稱相同,參數相同,但基類函數不是虛函數,父類函數被隱藏(和覆蓋的區別在於基類函數是否是虛函數)。
2.2個函數名稱相同,參數不同,無論基類函數是否是虛函數,基類函數都會被隱藏(和重載的區別在於兩個函數不在同一類中)。
七.例題
1.有這樣一個類: class Eye { public: void Look(void); }; 現在希望定義一個Head類,也想實現Look的功能,應該使用( )的方法,實現代碼重用。
A 繼承 B 組合 C 模板 D 多態
正確答案:B
2. 下面有關繼承、多態、組合的描述,說法錯誤的是?
A. 封裝,把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏
B. 繼承可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展
C. 隱藏是指派生類中的函數把基類中相同名字的函數屏蔽掉了
D. 覆蓋是指不同的函數使用相同的函數名,但是函數的參數個數或類型不同
正確答案: B D
3..下列關於模板的說法正確的是 ( )
A 模板的實參在任何時候都可以省略
B 類模板與模板類所指的是同一概念
C 類模板的參數必須是虛擬類型的
D 類模板中的成員函數不全是模板函數
正確答案: D