C++繼承


先上一段代碼:

#include<iostream>

using namespace std;

class Base{
public:
	int a;
protected:
	int b;
private:
	int c;
};

struct Derived:public Base{
};

int main(){
	Derived inst;
	cout<<sizeof(Base)<<endl;
	cout<<sizeof(Derived)<<endl;
	cout<<sizeof(inst)<<endl;
	cout<<inst.a<<endl;
	return 0;
}

輸出:

12
12
12
15487104

對應上面的代碼,先啰嗦幾個與“繼承”無關的語法:struct可以繼承自一個class;sizeof()可以施加在一個類型上面,也可以施加在一個對象上面;由於inst是局部變量,所以類中的普通類型數據成員是不被初始化的,所以輸出a=15487104。

我們看到sizeof(Base)==sizeof(Derived)==12,可見Derived繼承了Base的public、protected、private成員。

struct Derived:public Base{
public:
	void func(){
		cout<<b<<endl;
	}
};

int main(){
	Derived inst;
	inst.func();
	return 0;
}

一般在類外是不能訪問protected成員的,所以在main()函數中調用cout<<inst.b;中會發生編譯錯誤的,但是我們可以把對protected成員的訪問封閉在一個public方法中,通過調用public方法來間接訪問protected數據成員。

struct Derived:public Base{
public:
	void func(){
		cout<<c<<endl;
	}
};

上面代碼會有編譯錯誤,protected成員好歹在派生類中還可以訪問,private成員在派生類中都不能訪問。既然成員c在Derived中不能訪問,那sizeof(Derived)又何苦要等於12呢?以Derived分配了3個int的空間,但其中一個int卻是不可訪問的,有什么意義呢?

(1)公有繼承:基類成員保持自己的訪問級別:基類的public成員在派生類中還是public,基類的protected成員在派生類中還是protected。

(2)受保護繼承:基類的public和protected成員在派生類中成為protected成員。

(3)私有繼承:基類的所有成員在派生類中成為private成員。

struct Derived:private Base{
};

class Grandson:public Derived{
public:
	void func(){
		cout<<a<<endl;
	}
};

上面的代碼有編譯錯誤,由於Derived私有繼承自Base,所以a在Derived中已經是private了,在Grandson中不能訪問Derived中的私有成員。 

struct Derived:private Base{
public:
	using Base::b;
protected:
	using Base::a;
};

由於Derived私有繼承自Base,按說a、b、c在Derived中應該都是私有的,但通過上述方式,在Derived中b中公有的,a中受保護的。

struct Derived:Base

 Derived為struct時默認為公有繼承。

class Derived:Base

Derived為class時默認為私有繼承。

友元類可以訪問private和protected成員數據,友元關系不能繼承。

#include<iostream>

using namespace std;

class Base{
	friend class Frnd;
private:
	int a;
};

class Derived:public Base{
};

class Frnd{
public:
	void func(){
		Base base;
		cout<<base.a<<endl;    //OK
		Derived derived;
		cout<<derived.a<<endl;    //error
	}
};

虛函數

當父類中的某個函數標記為virtual時,子類中如果重載了該方法則自動也具有virtual屬性,不管你是否使用了"virtual“關鍵字。比如下面的代碼:

#include<iostream>

using namespace std;

class A{
public:
	virtual void func(){
		cout<<"A.func"<<endl;
	}
};

class B:public A{
public:
	void func(){			//雖然沒有顯式寫明virtual,但該函數依然是虛函數
		cout<<"B.func"<<endl;
	}
};

class C:public B{
public:
	virtual void func(){
		cout<<"C.func"<<endl;
	}
};

int main(){
	A *a=new A();
	A *b=new B();
	A *c=new C();
	a->func();
	b->func();
	c->func();
	return 0;
}

輸出:

A.func
B.func
C.func

內部類可以訪問外部類的所有成員。

class Base{
public:
	int a;
	class Inner{
	public:
		void func(){
			Base base;
			cout<<base.c<<endl;
		}
	};
protected:
	int b;
private:
	int c;
};

int main(){
	Base::Inner inst;
	inst.func();
	return 0;
}

注意在main函數中訪問Inner類時要加Base::前綴,另外Inner只有定義在Base的public域下時,在Base之外才可以訪問Inner。

#include<iostream>

using namespace std;

class Base{
public:
	Base(){
		cout<<"Base construct"<<endl;
	}
	virtual void foo(){
		cout<<"Base foo"<<endl;
	}
	~Base(){
		cout<<"Base destruct"<<endl;
	}
};

class Derived:public Base{
public:
	Derived(){
		cout<<"Derived construct"<<endl;
	}
	virtual void foo(){
		cout<<"Derived foo"<<endl;
	}
	~Derived(){
		cout<<"Derived destruct"<<endl;
	}
};

int main(){
	Base *inst=new Derived();		//調用Derived的構造函數,在此之前會調用Base的構造函數
	inst->foo(); //調用Derived的foo方法 
	delete inst; //只調用了Base的析構函數 
}

通常情況下Derived擁有比Base更多的數據成員,在Derived的析構函數中需要釋放更多的資源,但是我們看到上面的代碼中Base和Derived的構造函數都調用了,最后卻只調用了Base的析構函數。解決方法:Base中使用虛的析構函數。

#include<iostream>

using namespace std;

class Base{
public:
	Base(){
		cout<<"Base construct"<<endl;
	}
	virtual void foo(){
		cout<<"Base foo"<<endl;
	}
	virtual ~Base(){
		cout<<"Base destruct"<<endl;
	}
};

class Derived:public Base{
public:
	Derived(){
		cout<<"Derived construct"<<endl;
	}
	virtual void foo(){
		cout<<"Derived foo"<<endl;
	}
	~Derived(){
		cout<<"Derived destruct"<<endl;
	}
};

int main(){
	Base *inst=new Derived();  //調用Derived的構造函數,在此之前會調用Base的構造函數
	inst->foo();   //調用Derived的foo方法 
	delete inst;   //調用Derived的析構函數,在此之后會調用Base的析構函數 
}

最佳實踐:即便析構函數不做任何工作,繼承層次的根類也應該定義一個虛析構函數。

class Base{
public:
	double price(std::size_t) const =0;
};

在函數形參表后面寫上=0表示該函數為純虛函數,含有一個或多個純虛函數的類為抽象類,不能創建抽象類的實例。抽象類為后代提供了可以覆蓋的接口。

void func(Base &base);
Derived inst;
func(inst);

當把基類對象傳遞給func()函數時,引用base直接綁定到inst對象,沒有發生對象的復制,沒有發生類型的轉換,inst也沒有發生任何改變不。

void func(Base base);
Derived inst;
func(inst);

此時情況完全不同,形參的類型是固定的,在編譯和運行時都是基類類型對象,如果把派生類對象傳遞過去,則派生類對象中的基類部分會發生復制。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM