OOP筆記(八) 類的繼承與派生
1.概念
類的繼承:是新的類從已有類那裏得到已有的特性。
類的派生:從已有的類產生一個新類的過程。
基類(父類):原有的類
派生類(子類):產生的新類。
派生類同樣可以作爲基類派生新的類,這樣就形成了類的層次結構。類的派生實際是一種演化、發展過程,即通過擴展、更改和特殊化,從一個已知類出發建立一個新類。通過類的派生可以建立具有共同關鍵特徵的對象家族,從而實現代碼的重用,這種繼承和派生的機制對於已有程序的發展和改進極爲有利。
單繼承與多繼承
單繼承:一個派生類只有一個直接基類的情況,稱爲單繼承
多繼承:一個派生類同時有多個基類,稱爲多繼承
在派生過程中,派生出來的新類也同樣可以作爲基類再繼續派生新的類,此外,一個基類可以同時派生出多個派生類。即:
一個基類從父類繼承來的特徵可以被其他新的子類所繼承
一個父類的特徵,可以同時被多個子類繼承
這樣,就形成了一個相互關聯的類的家族,稱爲類族。
2.派生類的定義
派生類的生成過程
(1)吸收基類成員
在C++的類繼承中,第一步是將基類的成員全盤接收,這樣,派生類實際上就包含了它所有基類中除構造和析構函數之外的所有成員。
在派生過程中,構造函數和析構函數都不被繼承。
(2)改造基類成員
對基類成員的改造包括兩個方面:
A. 一個是基類成員的訪問控制問題
B. 一個是對基類數據或函數成員的覆蓋,就是在派生類中聲明一個和基類數據或函數同名的成員。
同名覆蓋:如果派生類聲明瞭一個和基類成員同名的新成員(如果是成員函數,則函數名要相同,參數不同屬於重載),派生的新成員就會覆蓋外層同名的成員。
1)在派生類中通過派生類的對象直接使用成員名就只能訪問到派生類中聲明的同名成員(而訪問不到基類中的同名成員),這稱爲同名覆蓋。
(3)添加新的成員
派生類新成員的加入是繼承與派生機制的核心,是保證派生類在功能上有所發展的關鍵。我們可以根據實際情況需要,給派生類添加適當的數據和函數成員,來實現必要的新增功能。
此外,由於基類的構造函數和析構函數不被繼承,還需要在派生類中重新加入新的構造和析構函數。
//基類
class Employee
{
private:
string m_Name;
int m_Age;
int m_Year;
string m_Depart;
public:
Employee(){}//構造函數
~Employee(){}//析構函數
void insertInfo(string name,int age,int year,string department);//錄入數據
int getAge()
{return m_Age;}
void printOn(); //輸出信息
bool retire(); //判定退休
};
//派生類
class Manager:public Employee //(1)吸收基類成員:在類繼承中,第一步是將基類成員全盤接收
{
private:
string m_Level; //(3)添加新的成員:級別
public:
Manager(){}//構造函數
~Manager(){}//析構函數
void insertInfo(string name,int age,int year,string department,string level); //(2)改造基類成員:同名覆蓋
};
3.派生類的訪問屬性
基類的公有和保護成員的訪問屬性在派生類中保持不變,而基類的私有成員不可訪問。
- 基類的公有成員和保護成員被繼承到派生類中仍然作爲派生類的公有成員和保護成員,派生類的其他成員可以直接訪問它們。
- 其他外部使用者能通過派生類的對象訪問繼承來的公有成員
- 但不論是派生類的成員還是對象都無法訪問基類的私有成員。
4.派生類的構造函數與析構函數
class B1
{
public:
B1(int i) //有參構造函數
{cout<<"構造B1 "<<i<<endl;}
};
class B2
{
public:
B2(int j) //有參構造函數
{cout <<"構造B2 "<<endl;}
};
class B3
{
public:
B3() //無參構造函數
{cout <<"構造B3 "<<endl;}
};
class C:public B1,public B3,public B2 //三個基類
{
public:
C(int a,int b,int c,int d) :B1(c),B2(b){} //必須初始化列表
};
int main()
{
C obj(1,2,3,4);
return 0;
}
調用基類構造函數的兩種方式
• 顯式方式:
派生類的構造函數中一定要爲基類的構造函數提供參數
派生類::派生類(形參數列表):基類(基類參數列表)
• 隱式方式:
1、派生類的構造函數中, 省略基類構造函數時,派生類的構造函數, 自動調用基類的默認構造函數
2、派生類的析構函數被執行時, 執行完派生類的析構函數後, 自動調用基類的析構函數
class Base { //基類
public:
int n;
Base(int i):n(i)
{ cout << "基類 " << n << " 構造" << endl; }
~Base()
{ cout << "基類" << n << "析構" << endl; }
};
class Derived:public Base { //派生
public:
Derived(int i):Base(i)
{cout << "派生類構造" << endl; }
~Derived()
{cout << “派生類析構” << endl;}
};
int main()
{
Derived Obj(3); return 0; }
class A
{
public:
A (int i)
{ x = i;
cout<<"A構造"<<endl; }
~A() { cout<<"析構"<<endl; }
int getX()
{
return x; }
private:
int x;
};
class B
{
public:
B(int i) : a1(i+10)
{ y = i;
cout<<"B 構造"<<endl; }
~B() { cout<<"B 析構"<<endl; }
void printB()
{cout<<"B's d, x = "<<a1.getX()<<endl;}
private:
int y;
A a1;//對象成員
};
class D : public B
{
public:
D(int i) : B(i-2), a2(i+2)
{ z = i;
cout<<"D 構造"<<endl; }
~D() { cout<<"D 析構"<<endl; }
void dispD(){}
private:
int z;
A a2;//對象成員
};
int main()
{
D d(2);
return 1;
}
(1)創建派生類的對象時, 執行派生類的構造函數之前:
A、調用 基類 的構造函數,按照基類繼承順序調用構造函數
初始化派生類對象中從基類繼承的成員
B、調用 成員對象類 的構造函數,按照成員對象的定義順序
初始化派生類對象中成員對象
執行完 派生類的析構函數 後:
C、調用 成員對象類 的析構函數
D、調用 基類 的析構函數
(2) 析構函數的調用順序與構造函數的調用順序相反