上章鏈接: 22.C++- 繼承與組合,protected訪問級別 繼承方式 繼承方式位於定義子類的”:”後面,比如: 繼承方式預設為private 在C++中,繼承方式共有3種: public繼承 -指父類的成員(變數和函數)訪問級別,在子類中保持不變 private繼承 -指父類的成員,在子類中 ...
上章鏈接: 22.C++- 繼承與組合,protected訪問級別
繼承方式
繼承方式位於定義子類的”:”後面,比如:
class Line : public Object //繼承方式是public {
};
繼承方式預設為private
在C++中,繼承方式共有3種:
public繼承
-指父類的成員(變數和函數)訪問級別,在子類中保持不變
private繼承
-指父類的成員,在子類中變為private私有成員.
-也就是說子類無法訪問父類的所有成員
protected繼承
-指父類的public成員 ,在子類中變為protected保護成員,其它成員級別保持不變
如下圖所示:
註意: protected繼承只針對子類有效
比如當父類是protected繼承時,則子類的子類就無法訪問父類的所有成員
一般而言,C++項目只用到public繼承
顯示調用父類構造函數
- 當我們創建子類對象時,編譯器會預設調用父類無參構造函數
- 若有子類對象,也會預設調用子類對象的無參構造函數。
比如以下代碼:
class StrA { public: StrA() { cout<<"StrA()"<<endl; } StrA(string s) { cout<<"StrA(string s):"<<s<<endl; } }; class StrB : public StrA { public: StrB(string s) { cout<<"StrB(int i):"<<s<<endl; } }; int main() { StrB b("123"); return 0; }
編譯運行:
StrA() //父類無參構造函數 StrB(int i):123
也可以通過子類構造函數的初始化列表來顯示調用
接下來,修改上面子類的StrB(string s)函數,通過初始化列表調用StrA(string s)父類構造函數
改為:
StrB(string s): StrA(s) { cout<<"StrB(int i):"<<s<<endl; }
運行列印:
StrA(string s):123 StrB(int i):123
父子間的同名成員和同名函數
- 子類可以定義父類中的同名成員和同名函數
- 子類中的成員變數和函數將會隱藏父類的同名成員變數和函數
- 父類中的同名成員變數和函數依然存在子類中
- 通過作用域分辨符(::)才可以訪問父類中的同名成員變數和函數
比如:
class Parent{ public: int mval; Parent() { mval=1000; }
void add(int i) { mval+=i; } }; class Child : public Parent { public: int mval; Child() { mval=100; } void add(int i,int j) { mval+=i+j; } };
在main()函數執行:
Child c; //c. add(10); //該行會報錯,由於子類有add函數,所以編譯器會預設在子類里尋找add(int i); c.Parent::add(10); //該行正確,執行父類的成員函數 c.add(2,3); cout<<"Child.mval="<<c.mval<<endl; cout<<"Parent.mval="<<c.Parent::mval<<endl;
列印:
Child.mval=105 Parent.mval=1010
從列印結果看到,父類和子類之間的作用域是不同的, 所以執行父類的同名成員變數和函數需要作用域分辨符(::)才行
父子間的相容
以上示例的Parent父類Child子類為例
- 子類對象可以直接賦值給父類對象使用,比如: Parent p; Child c; p=c;
- 子類對象可以初始化父類對象,比如: Parent p1(c);
- 父類引用可以直接引用子類對象,比如: Parent& p2 =c; //p2是c對象的別名
- 父類指針可以直接指向子類對象,比如: Parent* p3=&c;
其實是編譯器是將子類對象退化為了父類對象, 從而能通過子類來賦值初始化父類
所以上述的父類對象(包括指針/引用)也只能訪問父類中定義的成員.
如果父類對象想訪問子類的成員,只能通過強制轉換,將父類對象轉為子類類型
示例1,通過C方式轉換:
Child c; Parent* p3=&c; Child *c2 = (Child*)p3;
示例2,通過static_cast轉換:
Child c; Parent* p3=&c; Child *c2 = (static_cast*)<Child*>(p3);
虛函數
實現多態性,通過指向子類的父類指針或引用,可以訪問子類中同名覆蓋成員函數
首先參考下麵,沒有虛函數的示例:
class Parent { public: void print () { cout<<"class Parent"<<endl; } }; class Child : public Parent { public: void print () { cout<<"class Child"<<endl; } }; void example(Parent* p) { p ->print(); } int main() { Parent t; Child c; example(&t); example(&c); }
運行列印:
class Parent class Parent
從結果看出,即使example函數的指針p指向了Child c,也只能調用父類的example(),無法實現多態性.
所以C++引入了虛函數概念,根據指針指向的對象類型,來執行不同類的同名覆蓋成員函數,實現不同的形態
定義: 在父類成員函數的返回值前面,通過virtual關鍵字聲明,這樣便能訪問子類中的同名成員函數了
接下來將上個示例的父類成員函數example()改寫為虛函數:
virtual void print() //將父類的成員函數定為虛函數 { cout<<"class Parent"<<endl; }
運行列印:
class Parent class Child