注意:本文中代碼均使用 Qt 開發(fā)編譯環(huán)境
類的繼承與派生?
以原有的類為基礎(chǔ)產(chǎn)生新的類,我們就說新類繼承了原有類的特征铸敏,也可以說從原有類派生出新類。
類的繼承與派生好處悟泵?
提高了代碼的重用性和可擴展性杈笔。
派生新類的過程一般包括?
吸收已有類的成員糕非、調(diào)整已有類的成員和添加新的成員三個步驟蒙具。
在C++中,派生類的一般定義語法為朽肥?
class 派生類名:繼承方式 基類名1,繼承方式 基類名2,...,繼承方式 基類名n
{
派生類成員聲明; //是指除了從基類繼承來的所有成員之外禁筏,新增加的數(shù)據(jù)和函數(shù)成員
};
一個派生類可以同時有多個基類,這種情況稱為多繼承衡招。一個派生類只有一個基類的情況篱昔,成為單繼承。
單繼承可以看作是多繼承的一個最簡單的特例,多繼承可以看成是單繼承的組合州刽。
例子:
class employee {
protected:
char name[20]; // 姓名
int individualEmpNo; // 個人編號
int grade; // 級別
float accumPay; // 月薪總額
static int employeeNo; // 本公司職員編號目前最大值
public:
employee();
~employee();
void pay(); // 計算月薪函數(shù)
void promote(int); // 升級函數(shù)
void SetName(char *); // 設(shè)置姓名
char * GetName(); // 提取姓名
int GetindividualEmpNo(); // 提取編號
int Getgrade(); // 提取級別
float GetaccumPay(); // 提取月薪
};
class technician:public employee { // 兼職技術(shù)人員類
private:
float hourlyRate; // 每小時酬金 派生類新增
int workHours; // 當(dāng)月工作時數(shù) 派生類新增
public:
technician();
void SetworkHours(int wh); // 設(shè)置工作時數(shù) 派生類新增
void pay(); // 計算月薪函數(shù) 同名隱藏 隱藏基類
};
派生類過程:
1.吸收基類成員
這樣撩银,派生類實際上就包含了基類中除構(gòu)造函數(shù)和析構(gòu)函數(shù)之外的所有成員只嚣。在派生類的過程中構(gòu)造函數(shù)和析構(gòu)函數(shù)都不被繼承矗积。
2.改造基類成員
包括兩個方面:一個是基類成員的訪問控制問題客蹋,主要依靠派生類定義時的繼承方式來控制;第二個是對基類數(shù)據(jù)或函數(shù)成員的隱藏匹表,就是在派生類中聲明一個和基類數(shù)據(jù)或函數(shù)同名的成員门坷,例如例子中的pay()。此時使用成員名就只能訪問到派生類中聲明的同名成員袍镀,這稱作同名隱藏拜鹤。
3.添加新的成員
派生類新成員的加入是繼承與派生機制的核心,是保證派生類在功能上有所發(fā)展的關(guān)鍵流椒。
訪問控制###
類的繼承方式有public、protected明也、private三種宣虾,不同的繼承方式,導(dǎo)致原來具有不同訪問屬性的基類成員在派生類中的訪問屬性也有所不同温数。
這里所說的訪問來自兩個方面:
一是派生類中新增的成員訪問從基類繼承的成員绣硝;
二是在派生類外部(非類族內(nèi)的成員),通過派生類的對象訪問從基類繼承的成員撑刺。
<public>
當(dāng)類的繼承方式為公有繼承時鹉胖,基類的公有和保護成員的訪問屬性在派生類中不變,而基類的私有成員不可直接訪問够傍。
解釋:
基類的公有成員和保護成員被繼承到派生類中訪問屬性不變甫菠,仍作為派生類的公有成員和保護成員,派生類的其他成員可以直接訪問他們冕屯。
在類族之外只能通過派生類的對象訪問從基類繼承的公有成員寂诱,而無論是派生類的成員還是派生類的對象都無法直接訪問基類的私有成員。
示例:
// rectangle.h 文件
#ifndef RECTANGLE_H
#define RECTANGLE_H
class Point {
public:
void InitP(float xx = 0, float yy = 0) {
X = xx;
Y = yy;
}
void Move(float xOff,float yOff) {
X += xOff;
Y += yOff;
}
float GetX() { return X; }
float GetY() { return Y; }
private:
float X,Y;
};
class Rectangle : public Point {
public:
void InitR(float x,float y,float w,float h) {
InitP(x,y);
W = w;
H = h;
}
float GetH(){return H;}
float getW(){return W;}
private:
float W,H;
};
#endif // RECTANGLE_H
// .cpp 部分
#include <QCoreApplication>
#include <QDebug>
#include "rectangle.h"
int main()
{
Rectangle rect;
rect.InitR(2,3,20,10);
rect.Move(3,2);
qDebug() << "The data of rect(X,Y,W,H):"
<< rect.GetX() << "," << rect.GetY() << ","
<< rect.getW() << "," << rect.GetH();
return 0;
}
運行結(jié)果:
<private>
當(dāng)類的繼承方式為私有繼承時安聘,基類中的公有成員和保護成員都以私有成員身份出現(xiàn)在派生類中痰洒,而基類的私有成員在派生類中不可以直接訪問。
解釋:
基類的公有和保護成員被繼承后作為派生類的私有成員浴韭,派生類的其他成員可以直接訪問它們丘喻,但是在類族外部通過派生類的對象無法直接訪問它們。
無論是派生類的成員還是通過派生類的對象念颈,都無法直接訪問從基類繼承的私有成員泉粉。這也就是說,該種繼承方式舍肠,阻止了基類功能的繼續(xù)派生搀继。
示例:
//Rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
class Point {
public:
void InitP(float xx = 0, float yy = 0) {
X = xx;
Y = yy;
}
void Move(float xOff,float yOff) {
X += xOff;
Y += yOff;
}
float GetX() { return X; }
float GetY() { return Y; }
private:
float X,Y;
};
class Rectangle : private Point {
public:
void InitR(float x,float y,float w,float h) {
InitP(x,y);
W = w;
H = h;
}
void Move(float xOff,float yOff){
Point::Move(xOff,yOff);
}
float GetX(){return Point::GetX();}
float GetY(){return Point::GetY();}
float GetH(){return H;}
float getW(){return W;}
private:
float W,H;
};
#endif // RECTANGLE_H
// .cpp 部分與上一個示例中的相同
1.派生類的成員函數(shù)及對象無法直接訪問積累的私有成員(例如X窘面,Y)。
2.派生類的成員仍可以訪問到從基類繼承過來的公有和保護成員(例如在派生類函數(shù)成員InitR中直接調(diào)用基類的函數(shù)InitP)叽躯。
3.但是在類外部通過派生類的對象根本無法直接訪問到積累的任何成員财边,積累原有的外部接口(例如基類的GetX()和GetY()函數(shù))被派生類封裝和隱藏起來。
在私有繼承的情況下点骑,為了保證基類的一部分外部接口特征能夠在派生類中也存在酣难,就必須在派生類中重新聲明同名的成員。這里在派生類Rectangle中黑滴,重新聲明了Move憨募,GetX()和GetY()等函數(shù),利用派生類對基類成員的訪問能力袁辈,把基類的原有成員函數(shù)的功能照搬過來菜谣。
這種在派生類中重新聲明的成員函數(shù)具有比基類同名成員函數(shù)更小的作用域,因此在調(diào)用時晚缩,根據(jù)同名隱藏的原則尾膊,自然會調(diào)用派生類的函數(shù)。
<protected>
保護繼承中荞彼,基類的公有和保護成員都以保護成員的身份出現(xiàn)在派生類中冈敛,而基類的私有成員不可直接訪問。
解釋:
派生類的其他成員可以直接訪問從基類繼承來的公有和保護成員鸣皂,但是在類外部通過派生類的對象無法直接訪問它們抓谴。
無論是派生類的成員還是通過派生類的對象,都無法直接訪問從基類繼承的私有成員寞缝。
基類中的保護成員有可能被它的派生類訪問癌压,但是絕不可能被其他外部使用者(比如程序中的普通函數(shù)、與基類平行的其它類等)訪問荆陆。
示例1:
class A {
protected:
int x;
};
int main()
{
A a;
a.x = 5; // 錯誤措拇!
return 0;
}
在A類對象a的模塊中是無法訪問A類的保護成員的,這種情況下慎宾,保護成員和私有成員一樣得到了很好的隱藏丐吓。
示例2:
如果A類以公有方式派生產(chǎn)生了B類,則在B類中趟据,A類保護成員和該類的公有成員一樣是可以訪問的券犁。例如:
class A {
protected:
int x;
};
class B : public A {
public:
void function();
};
void B::function() {
x = 5; // 正確
}
int main()
{
B b;
b.function();
return 0;
}