通過特殊化已有的類來建立新類的過程,叫做“類的派生”, 原有的類叫做”基類”,新建立的類叫做“派生類”。
類的繼承是指派生類繼承基類的數(shù)據(jù)成員和成員函數(shù)。繼承用來表示類屬關系,不能將繼承理解為構成關系。
增加新的成員(數(shù)據(jù)成員和成員函數(shù))
重新定義已有的成員函數(shù)
改變基類成員的訪問權限
代碼格式:
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:
// 調(diào)用基類的構造函數(shù)對基類成員進行初始化
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 // 當定義一個派生類的對象時, 首先調(diào)用基類的構造函數(shù), 完成對基類成員的初始化
init Rect // 然后執(zhí)行派生類的構造函數(shù), 完成對派生類成員的初始化
x = 3, y = 4 // 調(diào)用基類成員函數(shù)showPoint();
w = 5, h = 6 // 調(diào)用派生類成員函數(shù)showRect();
delete Rect // 構造函數(shù)的執(zhí)行順序和構造函數(shù)的執(zhí)行順序相反, 首先調(diào)用派生類的析構函數(shù)
delete Point // 其次調(diào)用基類的析構函數(shù)
*/
}
如果希望Rect中的showRect()函數(shù)可以一次顯示x、y、w、h。我們直接修改showRect()函數(shù)是不行的。
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 {
// 公有數(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)用基類的構造函數(shù)對基類成員進行初始化
Rect(int a, int b, int c, int d):Point(a, b) {
w = c;
h = d;
cout << "init Rect" << endl;
}
/** 公有派生, Point類中的受保護數(shù)據(jù)成員, 在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ī)則
公有派生:
基類成員的訪問權限在派生類中基本保持不變。
基類的公有成員在派生類中仍然是公有的
基類的保護成員在派生類中仍然是受保護的
基類的不可訪問的成員在派生類中仍然是不可訪問的
基類的私有成員在派生類中變成了不可訪問的
總結:在公有派生的情況下,通過派生類自己的成員函數(shù)可以訪問繼承過來的公有和保護成員, 但是不能訪問繼承來的私有成員, 因為繼承過程的私有成員,變成了第四個級別,不可訪問的。
賦值兼容規(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:
// 調(diào)用基類的構造函數(shù)對基類成員進行初始化
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(); // 實際調(diào)用基類的show()函數(shù)
Point * p = &b; // 派生類對象的地址賦值給指向基類的指針
p -> show(); // 實際也是調(diào)用基類的show()函數(shù)
Rect * pb = &b; // 派生類指針
pb -> show(); // 調(diào)用派生類的show()函數(shù)
a = b; // 派生類對象的屬性值, 更新基類對象的屬性值
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
比如一個Person類,派生出一個Student類,我們可以說Student就是Person,也就是 Student isa Person,而反過來則不行。
一個類用另一個類的對象作為自己的數(shù)據(jù)成員或者成員函數(shù)的參數(shù) 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。
限制了數(shù)據(jù)成員和成員函數(shù)的訪問權限,因此在實際中保護派生用得也不多。
比如:我們在上個例子中,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, 私有繼承自類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中不可訪問, 基類B中公有成員setB(), showB()在派生類C中變私有. 在main()函數(shù)中不可訪問
// 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
*/
}
對基類成員的訪問必須是無二義性的,如果一個表達式的含義可以解釋為可以訪問多個基類中的成員,則這種對基類成員的訪問就是不確定的,稱這種訪問具有二義性。
代碼格式:
類名::標識符
:: 為作用域分辨符,"類名"可以是任一基類或派生類名,“標識符”是該類中聲明的任一成員名,
像這樣:
// 基類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 func
B func
B gunc
C gunc
C gunc // 如果基類中的名字在派生類中再次聲明, 則基類中的名字就被隱藏. 如果我們想要訪問被隱藏的基類中的成員則使用作用域分辨符B::gunc();
A func
B func
*/
}
如果派生類定義了一個同基類成員函數(shù)同名的新成員函數(shù)(具有相同參數(shù)表的成員函數(shù)),派生類的新成員函數(shù)就覆蓋了基類的同名成員函數(shù)。
在這里,直接使用成員名只能訪問派生類中的成員函數(shù),使用作用域運算符,才能訪問基類的同名成員函數(shù)。
派生類中的成員函數(shù)名支配基類中的同名的成員函數(shù)名,這稱為名字支配規(guī)則。
如果一個名字支配另一個名字,則二者之間不存在二義性,當選擇該名字時,使用支配者的名字。
例如上個例子中
c.gunc() // 輸出”C gunc”, 基類B中的gunc成員函數(shù)被支配了
c.B::gunc(); // 加上作用域分辨符, 來使用被支配的成員
-END-
來源 :老九學堂
推薦閱讀
免責聲明:本文內(nèi)容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!