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