通過特殊化已有的類來建立新類的過程,叫做“類的派生”, 原有的類叫做”基類”,新建立的類叫做“派生類”。
類的繼承是指派生類繼承基類的數據成員和成員函數。繼承用來表示類屬關系,不能將繼承理解為構成關系。
增加新的成員(數據成員和成員函數)
重新定義已有的成員函數
改變基類成員的訪問權限
代碼格式:
class 派生類名: 訪問控制 基類名 {private: 成員聲明列表protected: 成員聲明列表public: 成員聲明列表}
“冒號”表示新類是哪個基類的派生類;“訪問控制”指繼承方式。
三個方式:public、protected、private
// 基類class Point {int x;int y;public:Point(int a, int b) {x = a;y = b;cout << "init Point" << endl;}void showPoint() {cout << "x = " << x << ", y = " << y << endl;}~Point() {cout << "delete Point" << endl;}};// 派生類class Rect: public Point {int w;int h;public:// 調用基類的構造函數對基類成員進行初始化Rect(int a, int b, int c, int d):Point(a, b) {w = c;h = d;cout << "init Rect" << endl;}void showRect() {cout << "w = " << w << ", h = " << h << endl;}~Rect() {cout << "delete Rect" << endl;}};int main() {Rect r(3, 4, 5, 6);r.showPoint();r.showRect();/** 輸出結果init Point // 當定義一個派生類的對象時, 首先調用基類的構造函數, 完成對基類成員的初始化init Rect // 然后執(zhí)行派生類的構造函數, 完成對派生類成員的初始化x = 3, y = 4 // 調用基類成員函數showPoint();w = 5, h = 6 // 調用派生類成員函數showRect();delete Rect // 構造函數的執(zhí)行順序和構造函數的執(zhí)行順序相反, 首先調用派生類的析構函數delete Point // 其次調用基類的析構函數*/}
如果希望Rect中的showRect()函數可以一次顯示x、y、w、h。我們直接修改showRect()函數是不行的。
void showRect() {cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;}
報錯 error: 'x' is a private member of‘Point' 'y' is a private member of‘Point'
x, y為Point類的私有成員,公有派生時,在Rect類中是不可訪問的。
我們還需要將基類Point中的兩個成員聲明為protected的屬性。
像這樣:
// 基類class Point {// 公有數據成員protected:int x;int y;public:Point(int a, int b) {x = a;y = b;cout << "init Point" << endl;}void showPoint() {cout << "x = " << x << ", y = " << y << endl;}};// 派生類class Rect: public Point {int w;int h;public:// 調用基類的構造函數對基類成員進行初始化Rect(int a, int b, int c, int d):Point(a, b) {w = c;h = d;cout << "init Rect" << endl;}/** 公有派生, Point類中的受保護數據成員, 在Rect類中也是受保護的, 所以可以訪問 // 而通過公有繼承的基類私有的成員, 在派生類中是不可被訪問的 void showRect() {cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;}*/};int main() {Rect r(3, 4, 5, 6);r.showPoint();r.showRect();}
在根類中,對于成員的訪問級別有三種:public、protected、private
在派生類中,對于成員的訪問級別有四種:public(公有)、protected(受保護)、private(私有)、inaccessible(不可訪問)
(1)公有派生和賦值兼容規(guī)則
公有派生:
基類成員的訪問權限在派生類中基本保持不變。
基類的公有成員在派生類中仍然是公有的
基類的保護成員在派生類中仍然是受保護的
基類的不可訪問的成員在派生類中仍然是不可訪問的
基類的私有成員在派生類中變成了不可訪問的
總結:在公有派生的情況下,通過派生類自己的成員函數可以訪問繼承過來的公有和保護成員, 但是不能訪問繼承來的私有成員, 因為繼承過程的私有成員,變成了第四個級別,不可訪問的。
賦值兼容規(guī)則:
在公有派生的情況下, 一個派生類的對象可以作為基類的對象來使用的情況。
像這樣:
// 基類class Point {// 這里聲明成員屬性為受保護的protected:int x;int y;public:Point(int a, int b) {x = a;y = b;}void show() {cout << "x = " << x << ", y = " << y << endl;}};// 派生類class Rect: public Point {int w;int h;public:// 調用基類的構造函數對基類成員進行初始化Rect(int a, int b, int c, int d):Point(a, b) {w = c;h = d;}void show() {cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;}};int main() {Point a(1, 2);Rect b(3, 4, 5, 6);a.show();b.show();Point & pa = b; // 派生類對象初始化基類的引用pa.show(); // 實際調用基類的show()函數Point * p = &b; // 派生類對象的地址賦值給指向基類的指針p -> show(); // 實際也是調用基類的show()函數Rect * pb = &b; // 派生類指針pb -> show(); // 調用派生類的show()函數a = b; // 派生類對象的屬性值, 更新基類對象的屬性值a.show(); // 調用基類的show()函數/**x = 1, y = 2x = 3, y = 4, w = 5, h = 6x = 3, y = 4x = 3, y = 4x = 3, y = 4, w = 5, h = 6x = 3, y = 4*/}
(2)“isa”和”has-a“的區(qū)別
繼承和派生 isa
比如一個Person類,派生出一個Student類,我們可以說Student就是Person,也就是 Student isa Person,而反過來則不行。
一個類用另一個類的對象作為自己的數據成員或者成員函數的參數 has-a。
像這樣:
// 地址類class Address {};class PhoneNumber {};// 職工類class Worker {String name;Address address;PhoneNumber voiceNumber;};
表示一個Worker對象有一個名字,一個地址,一個電話號碼,has-a的關系,包含的關系。
(3)私有派生
通過私有派生,基類的私有和不可訪問成員在派生類中是不可訪問的,而公有和保護成員這里就成了派生類的私有成員。
// 基類class Point {int x;int y;public:Point(int a, int b) {x = a;y = b;}void show() {cout << "x = " << x << ", y = " << y << endl;}};// 派生類class Rect: private Point {int w;int h;public:Rect(int a, int b, int c, int d):Point(a, b) {w = c;h = d;}void show() {Point::show(); // 通過私有繼承, Point類中的公有成員show(), 在Rect中為私有cout << "w = " << w << ", h = " << h << endl;}};class Test: public Rect {public:Test(int a, int b, int c, int d):Rect(a, b, c, d) {}void show() {Rect::show();//Point::show();/** error: 'Point' is a private member of ‘Point’標明: 不可訪問基類Point中的成員Rect類私有繼承自Point類, 所以Point中的私有成員x, 私有成員y, 在Rect類中為不可訪問: Point類中公有成員show(), 在Rect中變私有Test類公有繼承自Rect類, 所以在Rect中成員x, 成員y, 仍然是不可訪問, Rect::show()還是public, 但是Point::show()不可訪問 */}};
因為私有派生不利于進一步派生, 因而實際中私有派生用得并不多。
(4)保護派生保護派生使原來的權限都降一級使用
即private變?yōu)椴豢稍L問,protected變?yōu)閜rivate,public變?yōu)閜rotected。
限制了數據成員和成員函數的訪問權限,因此在實際中保護派生用得也不多。
比如:我們在上個例子中,Rect類保護派生于Point,則在Test類中Point::show();就可以使用啦!
代碼格式:
class 派生類名: 訪問控制 基類名1, 訪問控制 基類名2, … {//定義派生類自己的成員}
像這樣:
// 基類A, 也叫根類class A {int a;public:void setA(int x) {a = x;}void showA() {cout << "a = " << a << endl;}};// 基類B, 也叫根類class B {int b;public:void setB(int x) {b = x;}void showB() {cout << "b = " << b << endl;}};// 多重繼承, 公有繼承自類A, 私有繼承自類Bclass C: public A, private B {int c;public:void setC(int x, int y) {c = x;setB(y);}void showC() {showB();cout << "c = " << c << endl;}};int main() {C c;c.setA(53); // 調用基類setA()函數c.showA(); // 調用基類showA()函數c.setC(55, 58); // 調用派生類C的setC()函數c.showC(); // 調用派生類C的showC()函數// 派生類C私有繼承自基類B, 所以基類B中私有成員b, 在派生類C中不可訪問, 基類B中公有成員setB(), showB()在派生類C中變私有. 在main()函數中不可訪問// c.setB(60); // error: 'setB' is a private member of 'B'// c.showB(); // 'showB' is a private member of 'B'/**a = 53b = 58c = 55*/}
對基類成員的訪問必須是無二義性的,如果一個表達式的含義可以解釋為可以訪問多個基類中的成員,則這種對基類成員的訪問就是不確定的,稱這種訪問具有二義性。
代碼格式:
類名::標識符
:: 為作用域分辨符,"類名"可以是任一基類或派生類名,“標識符”是該類中聲明的任一成員名,
像這樣:
// 基類A, 也叫根類class A {public:void func() {cout << "A func" << endl;}};// 基類B, 也叫根類class B {public:void func() {cout << "B func" << endl;}void gunc() {cout << "B gunc" << endl;}};// 多重繼承class C: public A, public B {public:void gunc() {cout << "C gunc" << endl;}void hunc() {/**這里就具有二義性, 它即可以訪問A類中的func(), 也可以訪問類B中的func()*///func(); // error: Member 'func' found in multiple base classes of different types}void hunc1() {A::func();}void hunc2() {B::func();}};int main() {C c;//c.func(); //具有二義性c.A::func();c.B::func();c.B::gunc();c.C::gunc();c.gunc();c.hunc1();c.hunc2();/** 輸出結果A funcB funcB guncC guncC gunc // 如果基類中的名字在派生類中再次聲明, 則基類中的名字就被隱藏. 如果我們想要訪問被隱藏的基類中的成員則使用作用域分辨符B::gunc();A funcB func*/}
如果派生類定義了一個同基類成員函數同名的新成員函數(具有相同參數表的成員函數),派生類的新成員函數就覆蓋了基類的同名成員函數。
在這里,直接使用成員名只能訪問派生類中的成員函數,使用作用域運算符,才能訪問基類的同名成員函數。
派生類中的成員函數名支配基類中的同名的成員函數名,這稱為名字支配規(guī)則。
如果一個名字支配另一個名字,則二者之間不存在二義性,當選擇該名字時,使用支配者的名字。
例如上個例子中
c.gunc() // 輸出”C gunc”, 基類B中的gunc成員函數被支配了c.B::gunc(); // 加上作用域分辨符, 來使用被支配的成員
-END-
來源 :老九學堂
推薦閱讀
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!






