天天看點

派生類

派生類

    • 派生,為什麼要派生?
      • 類型轉換
    • 派生類的構造函數
    • 派生類的析構函數
    • 派生類的通路
      • 當派生類中與基類有相同成員時:
      • 當派生類中無與基類有相同成員時:

派生,為什麼要派生?

派生的目的:

當新問題出現,原有的程式無法解決或者無法完全解決時,需要對原有的程式進行改造。

單繼承時派生的定義:文法:class 派生類名:繼承方式 基類名

繼承方式:

繼承方式存在三種,分别是公有繼承,私有繼承和保護繼承。

公有繼承:基類的public和protected成員通路屬性在派生類中保持不變,基類的private成員不可直接通路。

私有繼承:基類的public和protected成員以private身份出現在派生類中。

保護繼承:基類的public和protected成員以protected身份出現在派生類中,基類的private成員不可直接通路。派生類中的成員函數可以直接通路基類中的public和protected成員,但不能通路基類的private成員。通過派生類的對象不能直接通路從基類繼承的任何成員。

類型轉換

公有派生類對象可以被當做基類的對象使用,反之不可。因為公有派生類繼承了基類的東西,即派生類的内容比基類隻多不少,基類的對象通路接口和派生類的一樣,是以可以直接當做基類的對象使用。

派生類的對象可以隐含轉化為基類對象

派生類的對象可以初始化基類的引用

派生類的指針可以隐含轉化為基類的指針

通過基類的對象名,指針隻能使用從基類繼承的成員

派生類的構造函數

預設情況下:基類的構造函數不能被繼承,派生類需要自己定義構造函數。當基類有預設構造函數時,派生類構造函數可以不向基類傳遞參數,建立派生類的對象時,基類的預設構造函數被調用。而派生類繼承的需要初始參數的基類成員函數會由派生類傳遞參數給基類,再由基類的構造函數初始化這些成員函數,而派生類新增部分的初始化由函數體和初始化清單完成。

(c++11可以使用using語句繼承基類的構造函數,但隻能初始化從基類繼承的成員:

using 基類名 ::基類構造函數名)

單繼承時派生類構造函數文法:

派生類名::派生類名(基類初始化所需的形參,本類成員所需的形參):基類名(參數表),

本類成員初始化清單

{

其他初始化

}

如下Base2中的構造函數:

#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.初始化派生類的構造函數體中的内容。

以下程式進行構造順序的知識點加強:

#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;
}
           

運作結果:

派生類

派生類的析構函數

析構函數也不能被繼承,派生類如果需要,則需要自行聲明析構函數,聲明方式與無繼承關系時的析構函數相同。(不用顯示調用基類的析構函數,系統會自動隐式調用)

析構的順序與構造時正好相反。

構造時先構造基類繼承過來成員,再構造自身函數體的内容,而析構時先析構本類自己的成員,然後再析構繼承過來的成員(析構自己的成員時最後被構造的最先析構,析構繼承的成員時,最後繼承的最先被析構)

#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的順序。

派生類的通路

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

若未特别限定,則通過派生類對象使用的是派生類中的同名成員(同名隐藏規則),即預設通路多個同名函數中所屬派生類的那個。若要通過派生類對象通路基類中被隐藏的同名函數,可以使用基類名加作用域操作符::來限定。比如以下 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()則無二義性(同名隐藏原則)。

編寫程式的時候應該要避免出現二義性和備援問題(同時多次繼承同一個類出現資料重複等等諸類情況)。

繼續閱讀