派生類與繼承(C++學習筆記 31)
① 繼承,就是從先輩處得到屬性和行為特徵,類的繼承就是新的類從已有類那裡得到已有的特性。
② 類的派生,即可看作從已有類產生新類的過程。由已有類產生新類時,新類便包含了已有類的特徵,同時也可以加入自己的新特性。
③ 已有類稱為 基類 或 父類 ,產生的新類稱為 派生類 或 子類。派生類同樣也可以作為基類派生出新的類,這樣就形成了類的層次結構。
④ 類的繼承和派生,使程式設計師無需修改已有類,只需在已有類的基礎上,通過增加少量程式碼或修改少量程式碼的方法得到新的類,從而較好的解決程式碼重用的問題。
一、派生類的宣告
宣告一個派生類的一般格式為:
class 派生類名:[繼承方式] 基類名{
派生類新增的資料成員和成員函式
};
這裡的 “基類名” 是一個已經宣告的類的名稱,“派生類名” 是繼承原有類的特性而生成的新類的名稱。“繼承方式” 規定了如何訪問從基類繼承的成員,它可以是關鍵字 private,protected 或 public,分別表示私有繼承,保護繼承和公有繼承,如果不顯式地給出繼承方式關鍵字,系統預設為私有繼承。類的繼承方式指定了派生類成員以及類外物件對於從基類繼承來的成員的訪問許可權。
例 1 : 現有一個 Person 類,包含 name(姓名)、age(年齡)、sex(性別)等資料成員與成員函式 print:
#include<iostream>
using namespace std;
class Person{
public:
···
void print(){
cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl;
cout<<"sex:"<<sex<<endl;
}
protected: //保護成員可以被本類的成員函式訪問,也可以被本類的派生類的成員函式訪問
string name;
int age;
char sex;
};
要宣告一個 Employee 類,它包含有 name(姓名)、age(年齡)、sex(性別)、department(部門)及 salary(工資)等資料成員與成員函式 print1,如下所示:
class Employee:public Person{ //宣告派生類 Employee 公有繼承了基類 Person
public:
void print1(){ //新增加的函式成員
print();
cout<<"department:"<<department<<endl;
cout<<"salary:"<<salary<<endl;
}
private:
string department; //新增加的資料成員
float salary; //新增加的資料成員
};
二、派生類的構成
實際上,並不是把基類的成員和派生類新增加的成員簡單地加在一起就構成了派生類。構造一個派生類一般包括以下 3 部分工作:
(1)派生類從基類接收成員
在 C++ 的類繼承中,派生類把基類的全部成員(除建構函式和解構函式之外)接收過來。
(2)調整從基類接收的成員
派生類不能對接收基類的成員進行選擇,但是可以對這些成員進行某些調整。對基類成員的調整包括兩個方面:
① 改變基類成員在派生類中的訪問屬性,這主要是通過派生類宣告時的繼承方式來控制的。
② 派生類可以對基類的成員進行重定義,即在派生類中宣告一個與基類成員同名的成員,則派生類中的新成員會覆蓋基類的同名成員,這時在派生類中或者通過派生類物件,直接使用成員名就只能訪問到派生類中宣告的同名成員。
注意:在 ② 中,如果是成員函式,不僅應使函式名相同,而且函式的引數表也應相同,如果不相同,則稱派生類過載了基類的成員函式,而不是覆蓋了基類的同名函式。
(3)在派生類中增加新的成員
這體現了派生類對基類功能的擴充套件,是繼承和派生機制的核心,來實現必要的新增功能。
由於在繼承過程中,基類的建構函式和解構函式是不能被繼承的,因此在宣告派生類時,一般需要在派生類中定義新的建構函式和解構函式(當然,如果沒有定義則會呼叫系統自動預設生成的建構函式和解構函式)。
三、基類成員在派生類中的訪問屬性(最小原則)
類的繼承方式有 public(公有繼承)、protected(保護繼承)和 private(私有繼承)3種,不同的繼承方式導致不同訪問屬性的基類成員在派生類中的訪問屬性也不同。
在派生類中,從基類繼承來的成員可以按訪問屬性劃分為 4 種:不可直接訪問,公有(public),保護(protected)和私有(private)。
基類中的成員 | 在公有派生類中的訪問屬性 | 在私有派生類中的訪問屬性 | 在保護派生類中的訪問屬性 |
---|---|---|---|
私有成員 | 不可直接訪問 | 不可直接訪問 | 不可直接訪問 |
公有成員 | 公有 | 私有 | 保護 |
保護成員 | 保護 | 私有 | 保護 |
例如,當類的繼承方式為私有繼承時,基類中的所有公有成員在派生類中都以私有成員的身份出現。
四、派生類對基類成員的訪問規則
基類的成員可以有 public(公有)、protected(保護)和 private(私有)3種訪問屬性,基類的成員函式可以訪問基類中其他成員,但是在類外通過基類的物件,就只能訪問該基類的公有成員。
我們知道類的繼承方式有 public(公有繼承)、protected(保護繼承)和 private(私有繼承)3種。不同的繼承方式導致原來具有不同訪問屬性的基類成員在派生類中的訪問屬性也有所不同。
派生類對基類成員的訪問形式主要有以下兩種:
(1)內部訪問。由派生類中新增的成員函式對基類繼承來的成員的訪問。
(2)物件訪問。在派生類外部,通過派生類的物件對從基類繼承來的成員的訪問。
1、私有繼承的訪問規則
當類的繼承方式為私有繼承時,基類的公有成員和保護成員被繼承後作為派生類的私有成員,派生類的成員函式可以直接訪問它們,但是在類外部通過派生類的物件無法訪問。
基類的私有成員不允許派生類繼承,因此在私有派生類中是不可直接訪問的,所以無論是派生類成員函式還是通過派生類的物件,都無法直接訪問從基類繼承來的私有成員。
私有繼承的訪問規則:
基類中的成員 | 私有成員 | 公有成員 | 保護成員 |
---|---|---|---|
內部訪問 | 不可訪問 | 可訪問 | 可訪問 |
物件訪問 | 不可訪問 | 不可訪問 | 不可訪問 |
例 1:私有繼承的訪問規則
#include<iostream>
using namespace std;
class Base{ //宣告基類 Base
public:
void setx(int n){ //正確,成員函式 setx 可以訪問本類的私有成員 x
x=n;
}
void showx(){ //正確,成員函式 showx 可以訪問本類的私有成員 x
cout<<x<<endl;
}
private:
int x;
};
class Derived:private Base{ //宣告基類 Base 的私有派生類 Derived
private:
int y;
public:
void setxy(int n,int m){
setx(n); //基類的 setx 函式在派生類中為私有成員,派生類成員函式可以訪問
y=m; //正確,成員函式 setxy 可以訪問本類的私有成員 y
}
void showxy(){
// cout<<x; //錯誤,派生類成員函式不能直接訪問基類的私有成員
cout<<y<<endl;
}
};
int main(){
Derived obj;
// obj.setx(10); //錯誤,setx 在派生類中為私有成員,派生類物件不能訪問
// obj.showx(); //錯誤
obj.setxy(20,30); //正確
obj.showxy(); //正確
return 0;
}
例 2:私有繼承的訪問規則2
#include<iostream>
using namespace std;
class Base{ //宣告基類 Base
protected:
int a;
public:
void seta(int sa){ //正確,成員函式 seta 可以訪問本類的保護成員 a
a=sa;
}
void showa(){ //正確,成員函式 showa 可以訪問本類的保護成員 a
cout<<"a="<<a<<endl;
}
};
class Derive01:private Base{ //宣告基類 Base 的私有派生類 Derive01
protected:
int b;
public:
void setab(int sa,int sb){
a=sa; // a 在派生類中為私有成員,派生類成員函式可以訪問
b=sb;
}
void showab(){
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
}
};
class Derive02:private Derive01{ //宣告類 Derive01 的私有派生類 Derive02
private:
int c;
public:
void setabc(int sa,int sb,int sc){
setab(sa,sb);
c=sc;
}
void showabc(){
//cout<<"a="<<a<<endl; //錯誤,a 在類 Derive02 中為不可直接訪問成員
//cout<<"b="<<b<<endl; //正確,b 在類 Derive02 中為私有成員
showab();
cout<<"c="<<c<<endl;
}
};
int main(){
Base op1;
op1.seta(1);
op1.showa();
Derive01 op2;
op2.setab(2,3);
op2.showab();
Derive02 op3;
op3.setabc(4,5,6);
op3.showabc();
return 0;
}
2、公有繼承的訪問規則
基類中的成員 | 私有成員 | 公有成員 | 保護成員 |
---|---|---|---|
內部訪問 | 不可訪問 | 可訪問 | 可訪問 |
物件訪問 | 不可訪問 | 可訪問 | 不可訪問 |
例 3:公有繼承的訪問規則舉例
#include<iostream>
using namespace std;
class Base{
private:
int x;
protected:
int y;
public:
void setxy(int m,int n){
x=m;
y=n;
}
void showxy(){
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
}
};
class Derived:public Base{ //宣告基類 Base 的公有派生類 Derived
private:
int z;
public:
void setxyz(int m,int n,int l){
setxy(m,n); //函式 setxy 在派生類中是 public 成員,派生類成員函式可以訪問
z=l;
}
void showxyz(){
//cout<<"x="<<x<<endl; //錯誤,x 在類 Derived 中為不可直接訪問成員
//cout<<"y="<<y<<endl; //正確,y 在類 Derived 中為保護成員,派生類成員函式可以訪問
showxy();
cout<<"z="<<z<<endl;
}
};
int main(){
Derived obj;
obj.setxyz(30,40,50);
obj.showxy(); //正確,函式 showxy()在類 Derived 中為公有成員,派生類物件能訪問它
//obj.y=60; //錯誤,y 在類 Derived 中為保護成員,派生類物件不能訪問它
obj.showxyz();
return 0;
}
3、保護繼承的訪問規則
基類中的成員 | 私有成員 | 公有成員 | 保護成員 |
---|---|---|---|
內部訪問 | 不可訪問 | 可訪問 | 可訪問 |
物件訪問 | 不可訪問 | 不可訪問 | 不可訪問 |
例 4:保護繼承的訪問規則舉例
#include<iostream>
using namespace std;
class Base{ //宣告基類 Base
private:
int x;
protected:
int y;
public:
int z;
void setx(int i){
x=i;
}
int getx(){
return x;
}
};
class Derived:protected Base{
private:
int m;
protected:
int n;
public:
int p;
void setall(int a,int b,int c,int d,int e,int f);
void show();
};
void Derived::setall(int a,int b,int c,int d,int e,int f){
// x=a; //錯誤,改為 setx(a);
setx(a);
y=b;
z=c;
m=d;
n=e;
p=f;
}
void Derived::show(){
// cout<<"x="<<x<<endl; //錯誤,在派生類 Derived 中,x 為不可直接訪問成員
cout<<"x="<<getx()<<endl;
cout<<"y="<<y<<endl;
cout<<"z="<<z<<endl;
cout<<"m="<<m<<endl;
cout<<"n="<<n<<endl;
cout<<"p="<<p<<endl;
}
int main(){
Derived obj;
obj.setall(1,2,3,4,5,6);
obj.show();
//cout<<"y="<<obj.y<endl; //錯誤,y 為派生類 Derived 的保護成員,派生類物件不能訪問它
return 0;
}