# 繼承

# 概念

從一個或多個以前定義的類 (基類) 產生新類的過程稱為派生,這個新產生的類又稱為派生類。 類的繼承(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(); // 輸出歌星屬性值
};
// 模擬唱歌:播放 mp3 歌曲
void sing_star::playing(char *ps)
{
	char str[100]="play "; //play 後有空格
	strcat(str,ps);
	cout<<str;
	mciSendString(str,NULL,0,NULL);
	// 在 Dec-C++ 環境中还要进行设置:工具 \ 編譯器選項 \ 編譯器 \ 在連接器命令,	加入以下命令 \-lwinmm
	//mciSendStringA (str,NULL,0,NULL); //Windows API VC2008 调	用此函數
	//mciSendString (str,NULL,0,NULL); //Windows API VC6.0 調用此函數
	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); // 行走 10 步,1/4 秒走一步
	Jack.hearing("You are simple"); // 聽英文句子
	Jack.speek(1006); // 輸出整數 num 的英文句子
	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();
	// 葉老師  f       40      圖書館  2000
	cout<<endl;
	emp.ShowMe();
	// 葉老師  f       40
	cout<<endl;
	return 0;
}

# private 私有繼承方式

派生類對基類各種成員訪問權限如下:

  • 基類公有成員和保護成員都相當於派生類的私有成員,派生類只能通過自身的函數成員訪問他們
  • 對於基類的私有成員,無論派生類內部成員或派生類使用者都無法直接訪問。
...
// 雇員類定義
class Employee: private Person 
...
int main()// 主函式
{
	Employee emp;
	emp.EmployeeRegister("葉老師",40,'f',"圖書館",2000);
	emp.ShowEmp();
	// emp.ShowMe();
	// 本句違反繼承規定
	// Showme () 是派生類的私有成員,只能成員函數訪問,對象不能訪問
	// 報錯: 'Person' is not an accessible base of 'Employee'
	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();
	// stu.ShowMe();
	// 錯誤,對象不能訪問保護成員
	return 0;
}

# 小結

派生方式基類中的訪問限定在派生類中對基類成員的訪問限定外部函數 如 main()
公有繼承publicpublic可以直接訪問
protectedprotected不可以直接訪問
private不可以直接訪問不可以直接訪問
私有繼承publicprivate不可以直接訪問
protectedprivate不可以直接訪問
private不可以直接訪問不可以直接訪問
保護繼承publicprotected不可以直接訪問
protectedprotected不可以直接訪問
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;
}
//constructor of person 葉老師
//constructor of person Zrn
// constructor of Employee
// deconstrucor of Employee
//deconstrutor of person Zrn
//deconstrutor of person 葉老師
  • 構造函數的執行順序
    首先,調用基類構造函數,調用順序按照它們被繼承時聲明的基類名順序執行。
    其次,調用內嵌對象構造函數,調用次序按各個對像在派生類內聲明的順序。
    最後,執行派生類構造函數體中的內容。
  • 析構函數的執行順序
    派生類析構函數執行過程恰與構造函數執行過程相反。
    首先,執行派生類析構函數。
    然後,執行內嵌對象的析構函數。
    最後,執行基類析構函數。
更新於 閱讀次數

用實際行動犒賞爆肝的我😀

Zrn Ye LinePay

LinePay