繼承和派生的概念
繼承:在定義一個新的類B時,如果該類與某個已有的類A相似(指的是B擁有A的全部特點), 那麼就可以把A作爲一個基類,而把B作爲基類的一個派生類(也稱子類)。
- 派生類是通過對基類進行修改和擴充得到的。在派生類中,可以擴充新的成員變量 和成員函數。
- 派生類一經定義後,可以獨立使用,不依賴於基類。
- 派生類擁有基類的全部成員函數和成員變 量,不論是
private、protected、public
。
- 在派生類的各個成員函數中,不能訪問基類中的
private
成員。
- 在派生類的各個成員函數中,不能訪問基類中的
需要繼承機制的例子
- 所有學生都有的共同屬性:
- 姓名
- 學號
- 性別
- 成績
- 所有的學生都有的共同方法(成員函數):
- 是否該留級
- 是否該獎勵
- 而不同的學生,又有各自不同的屬性和方法
- 研究生
- 導師
- 系
- 大學生
- 系
- 中學生
- 競賽特長加分
- 研究生
- 如果爲每類學生都從頭編寫一個類,顯然會有不少重複的代碼,浪費。
- 比較好的做法是編寫一個“學生”類,概括了各種學生的共同特點,然後從“學生”類派 生出“大學生”類,“中學生”類,“研究生類”。
派生類的寫法
class 派生類名 : public 基類名 {
};
class CStudent {
private:
string sName;
int nAge;
public:
bool IsThreeGood() { };
void SetName( const string & name ) { sName = name; }
};
class CUndergraduateStudent : public CStudent {
private:
int nDepartment;
public:
bool IsThreeGood() { ...... }; //覆蓋
bool CanBaoYan() { .... };
}; // 派生類的寫法是:類名: public 基類名
class CGraduatedStudent : public CStudent {
private:
int nDepartment;
char szMentorName[20];
public:
int CountSalary() { ... };
};
派生類對象的內存空間
派生類對象的體積,等於基類對象的體積,再加上派生類對象自己的成員變量的體積。在派生類對象中,包含着基類對象,而且基類對象的存儲位置位於派生類對象新增的成員變量之前。
繼承實例程序:學籍管理
#include <iostream>
#include <string>
using namespace std;
class CStudent {
private:
string name;
string id; //學號
int age;
public:
void PrintInfo();
void SetInfo( const string & name_,const string & id_, int age_, char gender_ );
string GetName() { return name; }
};
void CStudent::PrintInfo() {
cout << "Name:" << name << endl;
cout << "ID:" << id << endl;
cout << "Age:" << age << endl;
cout << "Gender:" << gender << endl;
}
void CStudent::SetInfo( const string & name_,const string & id_, int age_,char gender_ ) {
name = name_;
id = id_;
age = age_;
gender = gender_;
}
class CUndergraduateStudent:public CStudent { //本科生類,繼承了CStudent類
private:
string department; //學生所屬的系的名稱
public:
void QualifiedForBaoyan() { //給予保研資格
cout << “qualified for baoyan” << endl;
}
void PrintInfo() {
CStudent::PrintInfo(); //調用基類的PrintInfo
cout << “Department:” << department <<endl;
}
void SetInfo( const string & name_,const string & id_,
int age_,char gender_ ,const string & department_) {
CStudent::SetInfo(name_,id_,age_,gender_); //調用基類的SetInfo
department = department_;
}
};
int main() {
CUndergraduateStudent s2;
s2.SetInfo(“Harry Potter ”, “118829212”,19,‘M’,“Computer Science”);
cout << s2.GetName() << “ ” ;
s2.QualifiedForBaoyan ();
s2.PrintInfo ();
return 0;
}
輸出結果:
Harry Potter qualified for baoyan
Name:Harry Potter
ID:118829212
Age:19
Gender:M
Department:Computer Science
繼承關係和複合關係
類之間的關係:
- 沒關係
- 繼承:“是”關係
- 基類A,B是基類A的派生類
- 邏輯上要求:“一個B對象也是一個A對象”。
- 複合:“有”關係
- 類C中“有”成員變量k,k是類D的對象,則C和D是複合關係
- 一般邏輯上要求:“D對象是C對象的固有屬性或組成部分”
複合關係的使用
幾何形體程序中,需要寫“點”類,也需要寫“圓”類,兩者的關係就是複合關係 —- 每一個“圓”對象裏都包含 (有)一個“點”對象,這個“點”對象就是圓心。
如果要寫一個小區養狗管理程序,需要寫一個“業主”類,還需要寫一個“狗”類。
而狗是有 “主人” 的,主人當然是業主(假定狗只有一個主人,但一個業主可以有最多10條狗)。
上面的寫法有循環定義的錯誤,無法確定兩個類所佔內存空間爲多大。
上面的寫法無法保證多個狗共同主人的數據統一問題。
此類寫法如果想調用某隻狗,只能通過其主人來進行,狗沒有自由。
基類/派生類同名成員與protected關鍵字
基類和派生類有同名成員的情況
void derived::access() {
j = 5; //error基類的私有成員
i = 5; //引用的是派生類的 i
base::i = 5; //引用的是基類的 i
func(); //派生類的
base::func(); //基類的
}
derived obj;
obj.i = 1;
obj.base::i = 1;
訪問範圍說明符
- 基類的
private
成員,可以被下列函數訪問:
- 基類的成員函數
- 基類的友元函數
- 基類的
public
成員,可以被下列函數訪問:
- 基類的成員函數
- 基類的友元函數
- 派生類的成員函數
- 派生類的友元函數
- 其他的函數
- 基類的
protected
成員,可以被下列函數訪問:
- 基類的成員函數
- 基類的友元函數
- 派生類的成員函數可以訪問當前對象的基類的保護成員
保護成員
class Father {
private:
int nPrivate; //私有成員
public:
int nPublic; //公有成員
protected:
int nProtected; // 保護成員
};
class Son : public Father {
void AccessFather () {
nPublic = 1; // ok;
nPrivate = 1; // wrong
nProtected = 1; // OK, 訪問從基類繼承的protected成員
Son f;
f.nProtected = 1; //wrong, f不是當前對象
}
};
int main() {
Father f;
Son s;
f.nPublic = 1; // Ok
s.nPublic = 1; // Ok
f.nProtected = 1; // error
f.nPrivate = 1; // error
s.nProtected = 1; // error
s.nPrivate = 1; // error
return 0;
}
派生類的構造函數
派生類的構造函數
- 派生類對象包含基類對象
- 執行派生類構造函數之前,先執行基類的構造函數
- 派生類交代基類初始化,具體形式:
構造函數名(形參表):基類名(基類構造函數實參表) {
}
class Bug {
private :
int nLegs;
int nColor;
public:
int nType;
Bug (int legs, int color);
void PrintBug () { };
};
Bug::Bug( int legs, int color) {
nLegs = legs;
nColor = color;
}
class FlyBug: public Bug { // FlyBug是Bug的派生類
int nWings;
public:
FlyBug(int legs, int color, int wings);
};
//錯誤的FlyBug構造函數:
FlyBug::FlyBug (int legs, int color, int wings) {
nLegs = legs; // 不能訪問
nColor = color; // 不能訪問
nType = 1; // ok
nWings = wings;
}
//正確的FlyBug構造函數:
FlyBug::FlyBug (int legs, int color, int wings):Bug(legs, color) {
nWings = wings;
}
int main() {
FlyBug fb ( 2,3,4);
fb.PrintBug();
fb.nType = 1;
fb.nLegs = 2 ; // error.nLegs is private
return 0;
}
- 在創建派生類的對象時
- 需要調用基類的構造函數,初始化派生類對象中從基類繼承的成員
- 在執行一個派生類的構造函數之前,總是先執行基類的構造函數
- 調用基類構造函數的兩種方式
- 顯示方式:派生類的構造函數中 -> 基類的構造函數提供參數
derived::derived(arg_derived-list):base(arg_base-list)
- 隱式方式:
- 派生類的構造函數中,省略基類構造函數時,派生類的構造函數,自動調用基類的默認構造函數
- 顯示方式:派生類的構造函數中 -> 基類的構造函數提供參數
- 派生類的析構函數被執行時,執行完派生類的析構函數後,自動調用基類的析構函數
class Base {
public:
int n;
Base(int i) : n(i) {
cout << "Base " << n << " constructed" << endl;
}
~Base() {
cout << "Base " << n << " destructed" << endl;
}
};
class Derived : public Base {
public:
Derived(int i) : Base(i) {
cout << "Derived constructed" << endl;
}
~Derived() {
cout << "Derived destructed" << endl;
}
};
int main() {
Derived Obj(3);
return 0;
}
輸出結果:
Base 3 constructed
Derived constructed
Derived destructed
Base 3 destructed
包含成員對象的派生類的構造函數
class Skill {
public:
Skill(int n) { }
};
class FlyBug: public Bug {
int nWings;
Skill sk1, sk2;
public:
FlyBug(int legs, int color, int wings);
};
FlyBug::FlyBug( int legs, int color, int wings): Bug(legs, color), sk1(5), sk2(color) {
nWings = wings;
}
- 創建派生類的對象 時, 執行派生類的構造函數之前:
- 調用基類的構造函數 -> 初始化派生類對象中從積累繼承的成員
- 調用成員對象類的構造函數 -> 初始化派生對象中的成員對象
- 執行完派生類的析構函數後
- 調用成員對象類的析構函數
- 調用基類的析構函數
- 析構函數的調用順序與構造函數的調用順序相反
public繼承的賦值兼容規則
class base { };
class derived : public base { };
base b;
derived d;
- 派生類的對象可以賦值給基類對象
b = d;
- 派生類對象可以初始化基類引用
base & br = d;
- 派生類對象的地址可以賦值給基類指針
base * pb = & d;
如果派生方式是 private
或protected
,則上述三條不可行。
直接基類和間接基類
類A派生類B,類B派生類C,類C派生類D,……
- 類A是類B的直接基類
- 類B是類C的直接基類,類A是類C的間接基類
- 類C是類D的直接基類,類A、B是類D的間接基類
在聲明派生類時,只需要列出它的直接基類,派生類沿着類的層次自動向上繼承它的間接基類。
派生類的成員包括:
- 派生類自己定義的成員
- 直接基類中的所有成員
- 所有間接基類的全部成員
#include <iostream>
using namespace std;
class Base {
public:
int n;
Base(int i):n(i) {
cout << "Base " << n << " constructed" << endl;
}
~Base() {
cout << "Base " << n << " destructed" << endl;
}
};
class Derived : public Base {
public:
Derived(int i):Base(i) {
cout << "Derived constructed" << endl;
}
~Derived() {
cout << "Derived destructed" << endl;
}
};
class MoreDerived : public Derived {
public:
MoreDerived():Derived(4) {
cout << "More Derived constructed" << endl;
}
~MoreDerived() {
cout << "More Derived destructed" << endl;
}
};
int main() {
MoreDerived Obj;
return 0;
}
輸出結果:
Base 4 constructed
Derived constructed
More Derived constructed
More Derived destructed
Derived destructed
Base 4 destructed