1. 繼承與派生
1.1繼承與派生的概念
在C++中,可重用性是通過繼承這一機制來實現(xiàn)的萎攒。所謂繼承遇八,就是在一個已存在的類的基礎(chǔ)上建立一個新的類。已存在的類稱為基類耍休,新建立的類成為派生類刃永。(與對象的復(fù)制做區(qū)別)一個新類從已有的類那里獲得其已有特性,這種現(xiàn)象稱為類的繼承羊精。
派生類繼承了基類的所有數(shù)據(jù)成員和成員函數(shù)斯够,并可以對成員作必要的增加或調(diào)整。
創(chuàng)一個小群喧锦,供大家學(xué)習(xí)交流聊天
如果有對學(xué)C++方面有什么疑惑問題的读规,或者有什么想說的想聊的大家可以一起交流學(xué)習(xí)一起進步呀。
也希望大家對學(xué)C++能夠持之以恒
C++愛好群燃少,
如果你想要學(xué)好C++最好加入一個組織束亏,這樣大家學(xué)習(xí)的話就比較方便,還能夠共同交流和分享資料阵具,給你推薦一個學(xué)習(xí)的組織:快樂學(xué)習(xí)C++組織 可以點擊組織二字碍遍,可以直達
單繼承:一個派生類只從一個基類派生。
多重繼承:一個派生類可以從多個基類派生怔昨。
基類和派生類的關(guān)系:派生類是基類的具體化雀久,而基類則是派生類的抽象。
1.2 派生類的聲明方式
聲明派生類的一般形式:
class 派生類名: 集成方式 積累名
{
派生類新增加的成員;
}
Eg:假設(shè)已經(jīng)聲明了一個基類Student
class Student1: public Student //聲明基類是Student趁舀,集成方式是public
{
public:
void display_1() //新增加的成員函數(shù)
{
cout<<”age:”<<age<<endl;
cout<<”address:”<<addr<<endl;
}
private:
int age; //新增加的數(shù)據(jù)成員
string addr;
};
繼承方式包括:public公用的赖捌,private私有的和protected受保護的。C++默認(rèn)為私有繼承。派生類會接收基類的所有成員越庇,無法選擇罩锐。但是可以改變積累成員在派生類中的訪問屬性。也可以通過同名覆蓋來使新成員取代基類成員卤唉。
1.3 派生類成員的訪問屬性
不管是何種繼承方式涩惑,基類的私有成員只有基類的成員函數(shù)才可以引用,派生類的成員函數(shù)是無法引用的桑驱。即基類的私有成員是不可見的竭恬,它在被派生類繼承后變?yōu)椴豢稍L問成員。
不能通過派生類的對象引用從私有基類繼承過來的任何成員熬的。派生類的成員函數(shù)不能訪問私有基類的私有成員痊硕,但可以訪問私有基類的公用成員。如何調(diào)用私有基類的公用成員函數(shù)如下:
void Student::display_1()//輸出學(xué)號押框、姓名岔绸、分?jǐn)?shù)、年紀(jì)和地址
{
display();//基類中的公用成員函數(shù)橡伞,目的是輸出學(xué)號盒揉、姓名和分?jǐn)?shù)
cout<<”age:”<<age<<endl;
cout<<”address:”<<addr<<endl;
}
int main()
{
Student stud1;
stud1.display();//在main中調(diào)用派生類中的公用成員函數(shù)
return 0;
}
回顧:保護成員不能被類外訪問,類似于私有成員兑徘。但有一點與私有成員不同刚盈,保護成員可以被派生類的成員函數(shù)引用!如果希望在派生類中可以訪問基類的“私有”成員道媚,則應(yīng)當(dāng)把它們聲明為保護成員扁掸。
在派生類中引用保護成員
#include<iostream>
#include<string>
using namespace std;
class Student //聲明基類
{
public:
void display();//基類共用成員函數(shù)
protected:
int num;//基類保護成員
string name;
char sex;
};
void Student::display()//定義基類成員函數(shù)
{
cout<<”num:”<<num<<endl;
cout<<”name:”<<name<<endl;
cout<<”sex:”<<sex<<endl;
}
class Student1: protected Student //用protected方式聲明派生類
{
public:
void display1();//聲明派生類共用成員函數(shù)
private:
int age;//派生類私有數(shù)據(jù)成員
string addr;
};
void Student1::display1()//定義派生類共用成員函數(shù)
{
display();
cout<<”age:”<<age<<endl;
cout<<”address:”<<addr<<endl;
}
int main()
{
Student1 stud1;
stud1.display();//合法,stud1是派生類中的共用成員函數(shù)
stud1.num=10023;//錯誤最域,外界不能訪問派生類的保護成員
return 0;
}
1.4 派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)
構(gòu)造函數(shù)的主要作用是對數(shù)據(jù)成員進行初始化,且基類的構(gòu)造函數(shù)是不能繼承的锈麸。對繼承過來的基類成員的初始化工作主要由派生類的構(gòu)造函數(shù)承擔(dān)镀脂。
一、簡單的派生類的構(gòu)造函數(shù)
簡單的派生類只有一個基類且只有一級派生忘伞,在派生類的數(shù)據(jù)稱成員中不包含基類的子對象薄翅。派生類構(gòu)造函數(shù)首行的寫法格式:
派生類構(gòu)造函數(shù)名 (總參數(shù)列表): 基類構(gòu)造函數(shù)名(參數(shù)列表)
{派生類中新增數(shù)據(jù)成員初始化語句}
Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)
例 1.10.2簡單的派生類的構(gòu)造函數(shù)
#include<iostream>
#include<string>
using namespace std;
class Student //聲明基類
public:
Student(int n,string nam,char s):num(n),name(nam),sex(s){}//基類構(gòu)造函數(shù)
~Student(){} //基類析構(gòu)函數(shù)
protected:
int num;
string name;
char sex;
};
class Student1: public Student //聲明派生類Student1
{
public:
Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)
//派生類構(gòu)造函數(shù)
{
age=a;在函數(shù)體中僅對派生類新增的數(shù)據(jù)成員進行初始化
addr=ad;
}
void show();
private:
int age;
string addr;
};
void Student1::show()
{
cout<<”num:”<<num<<endl;
cout<<”name:”<<name<<endl;
cout<<”sex:”<<sex<<endl;
cout<<”age:”<<age<<endl;
cout<<address:”<<addr<<endl;
}
int main()
{
Student1 stud1(10010,”Wang li”,’f’,19,”115 Beijing Road, Shanghai”);
Student1 stud2(10011.”Zhang fun”,’m’,21,”213 Shanghai Road, Beijing”);
stud1.show();
stud2.show();
return 0;
}
實參首先傳給派生類構(gòu)造函數(shù)的形參,然后派生類構(gòu)造函數(shù)再將前面幾個傳遞給基類構(gòu)造函數(shù)的形參氓奈。上例的具體過程見圖1.5翘魄。
圖 1.5 參數(shù)傳遞過程
在類內(nèi)聲明派生類的構(gòu)造函數(shù),然后在類外定義派生類的構(gòu)造函數(shù)
class Student1: protected Student
{
public:
Student1(int n,string nam,char s,int a,string ad);
};
Student1::Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)
//定義時才給出基類構(gòu)造函數(shù)名及其參數(shù)列表舀奶,聲明時不給出
{
age=a;
addr=ad;
}
二暑竟、有子對象的派生類構(gòu)造函數(shù)
類的數(shù)據(jù)成員還可包含對象,稱為子對象育勺。
格式:
派生類構(gòu)造函數(shù)名(總參數(shù)列表):基類構(gòu)造函數(shù)名(參數(shù)列表),子對象名(參數(shù)列表)
{派生類中新增加的數(shù)據(jù)成員初始化語句}
Student1(intn,string nam,int n1,string nam1,int a,string ad): Student(n,nam),monitor(n1,nam1)
執(zhí)行派生類構(gòu)造函數(shù)的順序:
① 調(diào)用基類構(gòu)造函數(shù)但荤,對基類數(shù)據(jù)成員初始化罗岖。
② 調(diào)用子對象構(gòu)造函數(shù),對子對象數(shù)據(jù)成員初始化腹躁。
③ 在執(zhí)行派生類構(gòu)造函數(shù)本身桑包,對派生類數(shù)據(jù)成員初始化。
在派生時纺非,派生類是不能夠繼承基類的析構(gòu)函數(shù)的哑了,也需要通過派生類的析構(gòu)函數(shù)去調(diào)用基類的析構(gòu)函數(shù)。
1.5 多重繼承
一個派生類有2個或多個基類烧颖,派生類從2個或多個基類中繼承所需的屬性垒手。如果已經(jīng)聲明了類A和類B,則可以聲明多重繼承的派生類C:
class C: public A, protected B
{類C新增加的成員};
一倒信、多重繼承派生類的構(gòu)造函數(shù)
類似于單繼承科贬,但是在初始表中包含多個基類構(gòu)造函數(shù)。多重繼承派生類構(gòu)造函數(shù)首行的寫法格式:
派生類構(gòu)造函數(shù)名(總參數(shù)列表):基類1構(gòu)造函數(shù)(參數(shù)列表),基類2構(gòu)造函數(shù)(參數(shù)列表)…
{派生類中新增數(shù)據(jù)成員初始化語句}
例 1.10.3聲明一個教師類和一個學(xué)生類鳖悠,然后用多重繼承的方式聲明一個研究生類榜掌。教師類包括數(shù)據(jù)成員name,age和title(職稱)乘综。學(xué)生類包括數(shù)據(jù)成員name1憎账,sex和score。
#include<iostream>
#include<string>
using namespace std;
class Teacher //聲明類Teacher
{
public:
Teacher(string nam,int a,string t):name(nam),age(a),title(t){}//構(gòu)造函數(shù)初始化表
void display();//輸出教師的數(shù)據(jù)
protected:
string name;
int age;
string title;
};
void Teacher::display()
{
cout<<”name:”<<name<<endl;
cout<<”age:”<<age<<endl;
cout<<”title:”<<title<<endl;
}
class Student //聲明類Student
{
public:
Student(char nam[],char s,float sco)
{
strcpy(name1,nam);
sex=s;
score=sco;
}
void display1();
protected:
string name1;
char sex;
float score;
};
void Student::display1()
{
cout<<”name:”<<name1<<endl;
cout<<”sex:”<<sex<<endl;
cout<<”score:”<<score<<endl;
}
class Graduate: public Teacher,public Student //聲明多重繼承的派生類Graduate
{
public:
Graduate(string nam,int a,char s,string t,float sco,float w): Teacher(nam,a,t), Student (nam, s, sco)
{wage=w;}
//多重繼承的派生類的構(gòu)造函數(shù)
void show();//輸出研究生有關(guān)數(shù)據(jù)
private:
float wage;//工資
};
void Graduate::show()
{
cout<<”name:”<<name<<endl;
cout<<”age:”<<age<<endl;
cout<<”sex:”<<sex<<endl;
cout<<”score:”<<score<<endl;
cout<<”title:”<<title<<endl;
cout<<”wages:”<<wage<<endl;
}
int main()
{
Graduate grad1(“Wang li”,24,’f’,”assistant”,89.5,1234.5);
grad1.show();
return 0;
}
二卡辰、多重繼承引起的二義性問題
繼承的成員同名會產(chǎn)生二義性問題胞皱。
同名覆蓋:基類的同名成員在派生類中被屏蔽,成為不可見的九妈,或者說反砌,派生類新增加的同名成員覆蓋了基類中的同名成員。解決這種情況需要虛函數(shù)萌朱。
如果類A和類B都是從一個基類派生的宴树,情況如下圖1.6:
圖 1.6 二義性問題的一種情況
解決這種情況需要用到虛基類。
1.6 虛基類
一晶疼、虛基類的聲明
由圖1.6可知酒贬,如果一個派生類有多個直接接類,而這些直接基類又有一個共同的基類翠霍,則在最終的派生類中會保留該間接共同基類數(shù)據(jù)成員的多份同名成員锭吨。為避免二義性,必須在派生類對象名后增加直接基類名寒匙,如c1.A::display()零如。C++提供虛基類的方法,使得在繼承間接共同基類時只保留一份成員。
聲明虛基類的一般形式:
class 派生類名: virtual 繼承方式 基類名
Eg:
class A //聲明基類A
{……};
class B: virtual public A //聲明類B是類A的共用派生類埠况,A是B的虛基類
{……};
虛基類不是在聲明基類時聲明的耸携,而是在聲明派生類時聲明的。virtual關(guān)鍵字不僅可以聲明虛基類辕翰,也用于聲明虛函數(shù)夺衍。在聲明派生類時,需要把virtual加到相應(yīng)的繼承方式前面喜命。經(jīng)過這樣的聲明后沟沙,當(dāng)基類通過多條派生路徑被一個派生類級城市,基類成員只保留一次壁榕。如圖1.7矛紫。
圖 1.7 虛基類的情況
為了保證虛基類在派生類中只被繼承一次,應(yīng)當(dāng)在該基類的所有直接派生類中聲明為虛基類牌里,否則仍會出現(xiàn)對基類的多次繼承颊咬。
二、虛基類的初始化
由于虛基類在派生類中只有一份數(shù)據(jù)成員牡辽,所以這份數(shù)據(jù)成員的初始化必須由派生類直接給出喳篇。在最后的派生類中,不僅需要負(fù)責(zé)對直接基類進行初始化态辛,也需要對虛基類進行初始化麸澜。
例 1.10.4 在例1.10.3的基礎(chǔ)上,在教師類和學(xué)生類之上添加一個共同的基類Person奏黑。
#include<iostream>
#include<string>
using namespace std;
class Person //聲明公共基類Peroson
{
public:
Person(string nam,char s,int a):name(name),sex(s),age(a){}//構(gòu)造函數(shù)
protected:
string name;
char sex;
int age;
};
class Teacher:virtual public Teacher
//聲明Person的直接派生類Teacher炊邦,且聲明Person為公用繼承的虛基類
{
public:
Teacher(string nam,char s,int a,string t):Person(nam,s,a)//構(gòu)造函數(shù)
{title=t;}
protected:
string title;
};
class Student:virtual public Person
//聲明Person的直接繼承類Student,且聲明Person為公用繼承的虛基類
{
public:
Student(string nam,char s,int a,float sco):Person(nam,s,a)
{score=sco;}
protected:
float score;
};
class Graduate: public Teacher,public Student //聲明多重繼承的派生類Graduate
{
public:
Graduate(string nam,char s,int a,string t,float sco,float w):Person(nam,s,a),Teacher(name, s,a,t),Student(nam,s,a,sco) //在派生類中的構(gòu)造函數(shù)熟史,對直接基類和間接基類進行初始化
{wage=w;}
void show();
private:
float wage;
};
void Graduate::show()
{
cout<<”name:”<<name<<endl;
cout<<”age:”<<age<<endl;
cout<<”sex:”<<sex<<endl;
cout<<”score:”<<score<<endl;
cout<<”title:”<<title<<endl;
cout<<”wages:”<<wage<<endl;
}
int main()
{
Graduate grad1(“Wang li”,’f’,24,”assistant”,89.5,1234.5);
grad1.show();
return 0;
}