派生類

派生,爲何要派生?

派生的目的
當新問題出現,原有的程序沒法解決或者沒法徹底解決時,須要對原有的程序進行改造。
單繼承時派生的定義:語法:class 派生類名:繼承方式 基類名
繼承方式:
繼承方式存在三種,分別是公有繼承,私有繼承和保護繼承。
公有繼承:基類的public和protected成員訪問屬性在派生類中保持不變,基類的private成員不可直接訪問。
私有繼承:基類的public和protected成員以private身份出如今派生類中。
保護繼承:基類的public和protected成員以protected身份出如今派生類中,基類的private成員不可直接訪問。派生類中的成員函數能夠直接訪問基類中的public和protected成員,但不能訪問基類的private成員。經過派生類的對象不能直接訪問從基類繼承的任何成員。ios

類型轉換

公有派生類對象能夠被當作基類的對象使用,反之不可。由於公有派生類繼承了基類的東西,即派生類的內容比基類只多很多,基類的對象訪問接口和派生類的同樣,所以能夠直接當作基類的對象使用。
派生類的對象能夠隱含轉化爲基類對象
派生類的對象能夠初始化基類的引用
派生類的指針能夠隱含轉化爲基類的指針
經過基類的對象名,指針只能使用從基類繼承的成員c++

派生類的構造函數

默認狀況下:基類的構造函數不能被繼承,派生類須要本身定義構造函數。當基類有默認構造函數時,派生類構造函數能夠不向基類傳遞參數,建立派生類的對象時,基類的默認構造函數被調用。而派生類繼承的須要初始參數的基類成員函數會由派生類傳遞參數給基類,再由基類的構造函數初始化這些成員函數,而派生類新增部分的初始化由函數體和初始化列表完成。
(c++11可使用using語句繼承基類的構造函數,但只能初始化從基類繼承的成員:
using 基類名 ::基類構造函數名)web

單繼承時派生類構造函數語法:
派生類名::派生類名(基類初始化所需的形參,本類成員所需的形參):基類名(參數表),
本類成員初始化列表
{
其餘初始化
}
以下Base2中的構造函數:svg

#include<iostream>
using namespace std;
class Base1{
	public:
		Base1(){
			b=0;
			cout<<"Base1 is constructed..."<<endl;
		}
		Base1(int i){
			b=i;
			cout<<"Base1 is constructed..."<<endl;
		}
		~Base1(){
			cout<<"Base1 is destructed..."<<endl; 
		}
		void print() const{
			cout<<b<<endl;
		}
	private:
		int b;
	
};
class Base2:public Base1{
	public:
		Base2(){
			c=0;
			cout<<"Base2 is constructed..."<<endl;
		}
		Base2(int i,int j):Base1(i),c(j){
			cout<<"Base2 is constructed..."<<endl;
		}
		~Base2(){
			cout<<"Base2 is destructed..."<<endl; 
		}
		void print() const{
			Base1::print();
			cout<<c<<endl;
		}
	private:
		int c;
	
};
int main(){
	Base2 b(1,2);
	b.print();
	return 0;
}

繼承時派生類將基類所需的初始化參數傳遞給基類進行初始化,而後初始化本身的列表。上述程序過程當中,生成Base2的對象,因爲有兩個參數,所以調用有兩個參數的構造函數Base2(int i,int j):Base1(i),c(j){},這個函數要向Base1的構造函數傳遞參數,所以先進行Base1的構造,運行結果以下:
在這裏插入圖片描述
由上述可知派生類構造函數的執行順序:函數

1.調用基類的構造函數,順序按照它們被繼承時聲明的順序,先聲明先構造。
2.對初始化列表中的成員進行初始化,順序按照它們在類中定義的順序,先定義先構造。
對象成員初始化時自動調用其所屬類的構造函數,由初始化列表提供參數。
3.初始化派生類的構造函數體中的內容。spa

如下程序進行構造順序的知識點增強:指針

#include<iostream>
using namespace std;
class Base1{
	public:
	Base1(int a){
		cout<<"Base1 is constructed..."<<a<<endl; 
	}
}; 
class Base2{
	public:
	Base2(int a){
		
		cout<<"Base2 is constructed..."<<a<<endl; 
	}
}; 
class Base3{
	public:
	Base3(){
		cout<<"Base3 is constructed..."<<endl; 
	}
}; 
class Depre:public Base1,public Base2,public Base3{
	public: 
	Depre(int a,int b,int c,int d):Base1(a),Base2(b),number1(c),number2(d){}
	private:
		Base1 number1;
		Base2 number2;
		Base3 number3;
};
int main(){
	Depre d(5,2,6,7);
	return 0;
}

運行結果:
在這裏插入圖片描述c++11

派生類的析構函數

析構函數也不能被繼承,派生類若是須要,則須要自行聲明析構函數,聲明方式與無繼承關係時的析構函數相同。(不用顯示調用基類的析構函數,系統會自動隱式調用)
析構的順序與構造時正好相反。
構造時先構造基類繼承過來成員,再構造自身函數體的內容,而析構時先析構本類本身的成員,而後再析構繼承過來的成員(析構本身的成員時最後被構造的最早析構,析構繼承的成員時,最後繼承的最早被析構)code

#include<iostream>
using namespace std;
class Base1{
	public:
	Base1(int a){
		cout<<"Base1 is constructed..."<<a<<endl; 
	}
	~Base1(){
		cout<<"Base1 is destructed..."<<endl; 
	} 
}; 
class Base2{
	public:
	Base2(int a){
		
		cout<<"Base2 is constructed..."<<a<<endl; 
	}
	~Base2(){
		cout<<"Base2 is destructed..."<<endl; 
	} 
}; 
class Base3{
	public:
	Base3(){
		cout<<"Base3 is constructed..."<<endl; 
	}
	~Base3(){
		cout<<"Base3 is destructed..."<<endl; 
	} 
}; 
class Depre:public Base1,public Base2,public Base3{
	public: 
	Depre(int a,int b,int c,int d):Base1(a),Base2(b),number1(c),number2(d){}
	private:
		Base1 number1;
		Base2 number2;
		Base3 number3;
};
int main(){
	Depre d(5,2,6,7);
	return 0;
}

運行結果以下:
在這裏插入圖片描述
如上所示,先析構了本身本類的成員,最後構造的最早析構,按number3,number2,number1的順序,再析構繼承過來的成員,最後繼承的最早析構,按Base3,Base2,Base1的順序。xml

派生類的訪問

當派生類中與基類有相同成員時:

若未特別限定,則經過派生類對象使用的是派生類中的同名成員(同名隱藏規則),即默認訪問多個同名函數中所屬派生類的那個。若要經過派生類對象訪問基類中被隱藏的同名函數,可使用基類名加做用域操做符::來限定。好比如下 d.Base1::fun();,實現調用基類被隱藏的fun()函數。

#include<iostream>
using namespace std;
class Base1{
	public:
	int var;
	void fun(){
		cout<<"number of Base1"<<endl;
	} 
};
class Base2{
	public:
	int var;
	void fun(){
		cout<<"number of Base2"<<endl;
	} 
};
class Derived:public Base1,public Base2{
	public:
		int var;
		void fun(){
			cout<<"number of Derived"<<endl;
		}
}; 
int main(){
	Derived d;
	d.fun();//同名隱藏原則,默認調用派生類的同名函數
	d.Base1::fun();
}

運行結果以下:
在這裏插入圖片描述

當派生類中無與基類有相同成員時:

同時從不一樣基類繼承了同名成員時訪問成員存在二義性問題,即繼承以後,這些同名函數如何區分,如何訪問成了問題。(能夠採用用類名限定來解決二義性的問題)

class A{
	public:
	void q();
};
class B{
	public:
	void q();
	void w();
};
class C:public A,public B{
	public:
	void w();
	void e();
};

若是此時定義一個C c;,那麼c.q();就存在二義性問題,而c.w()則無二義性(同名隱藏原則)。

編寫程序的時候應該要避免出現二義性和冗餘問題(同時屢次繼承同一個類出現數據重複等等諸類狀況)。