# 繼承
# 概念
從一個或多個以前定義的類 (基類) 產生新類的過程稱為派生,這個新產生的類又稱為派生類。 類的繼承(inheritance)是指新類從基類那裡得到基類的特徵,也就是繼承基類的數據和函數。
派生的新類同時也可以增加或重新定義數據和函數,這就產生了類的層次性
派生和繼承的概念也來自於人們認識客觀世界的過程
# 好處
- 軟件復用是軟件設計中常用的手段
- 在程序設計中反複使用高質量的軟件來縮短開發時間,提高效率(數量和質量)
- 客觀世界中許多實體之間是有繼承特性的
# 派生類
採用已存在的類去定義建立新類
新類稱為派生類(子類)
已存在的類稱為基類(父類)
派生類與基類具有相對性
# 定義方式
| class <派生類名>: <訪問權限> <基類名1>,......<訪問權限> <基類名n> |
| { |
| private: |
| <新增私有數據成員和成員函數> |
| protected: |
| <新增保護數據成員和成員函效> |
| public: |
| <新增公有數據成員和成員函效> |
| }; |
- 派生出新類時,可以做如下幾種變化:
- 可以增加新的數據成員
- 可以增加新的函數成員
- 可以重新定義已有的函數成員
- 可以改變現有成員的數據值
- 派生類的作用:
- 從基類接受成員
- 派生類對基類的擴充
- 派生類對基類成員的改造
- 系統的默認值就是私有繼承
| class sing_star:public person |
| { |
| protected: |
| float salary; |
| public: |
| sing_star(); |
| sing_star(char *n,char s,char *p,int w,int h,float s1); |
| void change_data(char *n,char s,char *p,int w,int h,float s1); |
| void playing(char *ps); |
| void print(); |
| }; |
| |
| |
| void sing_star::playing(char *ps) |
| { |
| char str[100]="play "; |
| strcat(str,ps); |
| cout<<str; |
| mciSendString(str,NULL,0,NULL); |
| |
| |
| |
| char a; |
| cin>>a; |
| } |
# 內嵌對象
| class Date |
| { |
| protected: |
| int year |
| int month; |
| int day; |
| public: |
| Date() |
| { |
| year = 1900; |
| month = day = 1; |
| } |
| Date(int yy,int mm,int dd) |
| { |
| init(yy,mm,dd); |
| }; |
| void init(int,int,int ); |
| void print_ymd(); |
| void print_mdy(); |
| }; |
| class Time |
| { |
| protected: |
| int hour; |
| int miniter; |
| int second; |
| public: |
| Time() |
| { |
| hour = miniter = second = 0; |
| } |
| Time(int h,int m,int s) |
| { |
| init(h,m,s); |
| }; |
| void init(int,int,int); |
| void print_time(); |
| }; |
| class person:public Date,public Time |
| { |
| |
| char name[20]; |
| char sex; |
| char pid[19]; |
| int weight; |
| int hight; |
| public: |
| person(); |
| person(char *n,char s,char *p,int w,int h,int hr,int mr,int sd); |
| void change_data(char *n,char s,char *p,int w,int h,int hr,int mr,int sd); |
| void walking(int k); |
| void hearing(char *sentence); |
| void speek(); |
| void writing(); |
| void ShowMe(); |
| }; |
| |
| |
| person::person() |
| { |
| name=new char[strlen("XXXXXX")+1]; |
| strcpy(name,"XXXXXX"); |
| strcpy(pid,"XXXXXXXXXXXXXXXXXX"); |
| sex='X'; |
| weight=0; |
| high=0; |
| year=1900; |
| month=day=1; |
| hour=miniter=second=0; |
| } |
| |
| person::person(char *n,char s,char *p,int w,int hh,int hr,int mr,int sd) |
| { |
| change_data(n,s,p,w,hh,hr,mr,sd); |
| } |
| |
| |
| void person::change_data(char *n,char s,char *p,int w,int hh,int hr,int mr,int sd) |
| { |
| name=new char[strlen(n)]; |
| strcpy(name,n); |
| strcpy(pid,p); |
| sex=s; |
| weight=w; |
| high=hh; |
| char temp[5]; |
| strncpy(temp,p+6,4); |
| year=atoi(temp); |
| strncpy(temp,p+10,2); |
| temp[2]='\0'; |
| month=atoi(temp); |
| strncpy(temp,p+12,2); |
| temp[2]='\0'; |
| day=atoi(temp); |
| hour=hr; |
| miniter=mr; |
| second=sd; |
| } |
| |
| |
| int main() |
| { |
| |
| person Jack("James Chen",'M',"610103198901062493",160,180,23,34,35); |
| Jack.print(); |
| system("pause"); |
| Jack.walking(10,4); |
| Jack.hearing("You are simple"); |
| Jack.speek(1006); |
| cout<<endl; |
| return 0; |
| } |
# 三種繼承方式
# public
公有繼承方式
派生類對基類各種成員訪問權限如下:
- 基類公有成員相當於派生類的公有成員,即派生類可以像訪問自身公有成員一樣訪問從基類繼承的公有成員
- 基類保護成員相當於派生類的保護成員,即派生類可以像訪問自身的保護成員一樣,訪問基類的保護成員
- 派生類內部成員無法直接訪問基類的私有成員
| #include <iostream> |
| #include <cstring> |
| using namespace std; |
| |
| |
| class Person |
| { |
| protected: |
| char Name[10]; |
| int Age; |
| char Sex; |
| public: |
| void Register(char *name, int age, char sex) |
| { |
| strcpy(Name, name); |
| Age = age; |
| Sex=(sex=='m'? 'm':'f'); |
| } |
| void ShowMe() |
| { |
| cout<<Name<<"\t"<<Sex<<"\t"<<Age<<"\t"; |
| } |
| }; |
| |
| |
| class Employee: public Person |
| { |
| char Dept[20]; |
| float Salary; |
| public: |
| Employee() |
| { |
| EmployeeRegister("XXX",0,'m',"XXX",0); |
| } |
| void EmployeeRegister(char *name, int age, char sex, char *dept, float salary); |
| void ShowEmp(); |
| }; |
| |
| void Employee::EmployeeRegister(char *name, int age, char sex, char *dept, float salary) |
| { |
| Register(name,age,sex); |
| strcpy(Dept, dept); |
| Salary = salary; |
| } |
| |
| void Employee::ShowEmp() |
| { |
| cout<<Name<<"\t"<<Sex<<"\t"<<Age<<"\t"; |
| cout<<Dept<<"\t"<<Salary; |
| } |
| |
| int main() |
| { |
| Employee emp; |
| emp.EmployeeRegister("葉老師",40,'f',"圖書館",2000); |
| emp.ShowEmp(); |
| |
| cout<<endl; |
| emp.ShowMe(); |
| |
| cout<<endl; |
| return 0; |
| } |
# private
私有繼承方式
派生類對基類各種成員訪問權限如下:
- 基類公有成員和保護成員都相當於派生類的私有成員,派生類只能通過自身的函數成員訪問他們
- 對於基類的私有成員,無論派生類內部成員或派生類使用者都無法直接訪問。
| ... |
| |
| class Employee: private Person |
| |
| ... |
| |
| int main() |
| { |
| Employee emp; |
| emp.EmployeeRegister("葉老師",40,'f',"圖書館",2000); |
| emp.ShowEmp(); |
| |
| |
| |
| |
| return 0; |
| } |
# protected
保護繼承方式
派生類對基類各種成員訪問權限如下:
- 基類的公有成員和保護成員都相當於派生類的保護成員,派生類可以通過自身的成員函數或其子類的成員函數訪問他們
- 對於基類的私有成員,無論派生類內部成員或派生類使用者都無法直接訪問
| class Student : protected Person |
| { |
| protected: |
| int Number; |
| char ClassName[10]; |
| public: |
| void Register(char *classname, int number, char *name, int age, char sex) |
| { |
| strcpy(ClassName, classname); |
| Number = number; |
| strcpy(Name, name); |
| Age = age; |
| Sex = (sex == 'm'?'m':'f'); |
| } |
| void ShowStu() |
| { |
| cout << Number << '\t' << ClassName << '\t'; |
| ShowMe(); |
| } |
| }; |
| |
| int main() |
| { |
| Student stu; |
| stu.Register("電腦51",85071011,"葉老師",18,'m'); |
| stu.ShowStu(); |
| |
| |
| return 0; |
| } |
# 小結
派生方式 | 基類中的訪問限定 | 在派生類中對基類成員的訪問限定 | 外部函數 如 main() |
---|
公有繼承 | public | public | 可以直接訪問 |
protected | protected | 不可以直接訪問 |
private | 不可以直接訪問 | 不可以直接訪問 |
私有繼承 | public | private | 不可以直接訪問 |
protected | private | 不可以直接訪問 |
private | 不可以直接訪問 | 不可以直接訪問 |
保護繼承 | public | protected | 不可以直接訪問 |
protected | protected | 不可以直接訪問 |
private | 不可以直接訪問 | 不可以直接訪問 |
# 派生類構造和析構函數
基類的構造函數與析構函數不能被繼承
派生類構造函數的一般形式為:
| <派生類名>::<派生類名>(<參數總表>): <基類名1>(<參數表1>),...,<基類名n>(<參數表n> ),<內嵌對象名1>(<對象參數表1>),...,<內嵌對象名m>(<對象參數表m>) |
| { |
| <派生類新增加成員的初始化>; |
| } |
# 執行順序
| #include<iostream> |
| #include<string.h> |
| using namespace std; |
| class Person |
| { |
| char Name[10]; |
| int Age; |
| public: |
| Person(char* name,int age) |
| { |
| strcpy(Name, name); |
| Age = age; |
| cout<<"constructor of person"<<Name<<endl; |
| } |
| ~Person() { |
| cout<<"deconstrutor of person"<<Name<<endl; |
| } |
| }; |
| |
| class Employee: public Person |
| { |
| char Dept[20]; |
| Person Leader; |
| public: |
| Employee(char *name, int age, char *dept, char *name1, int age1):Person(name,age), Leader(name1,age1) |
| { |
| strcpy(Dept, dept); |
| cout<<"constructor of Employee"<<endl; |
| } |
| ~Employee() { |
| cout<<"deconstrucor of Employee"<<endl; |
| } |
| }; |
| |
| int main() |
| { |
| Employee emp("葉老師",40,"學務處","Zrn",36); |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
- 構造函數的執行順序
首先,調用基類構造函數,調用順序按照它們被繼承時聲明的基類名順序執行。
其次,調用內嵌對象構造函數,調用次序按各個對像在派生類內聲明的順序。
最後,執行派生類構造函數體中的內容。 - 析構函數的執行順序
派生類析構函數執行過程恰與構造函數執行過程相反。
首先,執行派生類析構函數。
然後,執行內嵌對象的析構函數。
最後,執行基類析構函數。