亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++多重繼承及多態(tài)性原理實例詳解

 更新時間:2020年07月01日 08:32:15   作者:暖浮生  
這篇文章主要介紹了C++多重繼承及多態(tài)性原理實例詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

一、多重繼承的二義性問題

舉例:

#include <iostream>
using namespace std;
class BaseA {
public:	void fun() { cout << "A.fun" << endl; }
};
class BaseB {
public:
	void fun() { cout << "B.fun" << endl; }
	void tun() { cout << "B.tun" << endl; }
};
class Derived :public BaseA, public BaseB {
public:
	void tun() { cout << "D.tun" << endl; }
	void hun() { fun(); } //此處調(diào)用出現(xiàn)二義性,編譯無法通過
};
int main() {
	Derived d, * p = &d; d.hun();
	return 0;
}

類名限定

void hun() { BaseA::fun(); } //改寫上述代碼的第14行,用BaseA類名限定調(diào)用的函數(shù)
d.BaseB::fun(); //派生類對象調(diào)用基類同名函數(shù)時,使用類名限定
p->BaseB::fun(); //派生類指針調(diào)用基類同名函數(shù)時,使用類名限定

名字支配規(guī)則

如果存在兩個或多個包含關系的作用域,外層聲明了一個名字,而內(nèi)層沒有再次聲明相同的名字,則外層名字在內(nèi)層可見;如果在內(nèi)層聲明了相同的名字,則外層名字在內(nèi)層不可見——隱藏(屏蔽)規(guī)則。

在類的派生層次結(jié)構(gòu)中,基類的成員和派生類新增的成員都具有類作用域,二者的作用域不同:基類在外層,派生類在內(nèi)層。如果派生類聲明了一個和基類成員同名的新成員,則派生類的新成員就會屏蔽基類的同名成員,直接使用成員名只能訪問到派生類新增的成員。(如需使用從基類繼承的成員,應當使用基類名限定)

#include <iostream>
using namespace std;
class Base {
public:
	void fun() { cout << "A.fun" << endl; }
	Base(int x = 1, int y = 2) :x(x), y(y) {}
	int x, y;
};

class Derived :public Base{
public:
	void tun() { cout << "D.tun" << endl; }
	void fun() { cout << "D.fun" << endl; }
	Derived(int x = 0) :x(x) {}
	int x;
};
int main() {
	Derived d, * p = &d;
	d.fun();	//輸出的結(jié)果為	D.fun
	cout << p->x << " " << p->y << " " << p->Base::x << endl; //輸出為 0 2 1
	d.Base::fun(); //輸出為	A.fun
}

二、虛基類

虛基類的使用目的:在繼承間接基類時只保留一份成員。

聲明虛基類需要在派生類定義時,指定繼承方式的時候聲明,只需要在訪問標號(public、protected、private繼承方式)前加上virtual關鍵字。注意:為了保證虛基類在派生類中只繼承依次,應當在該基類的所有直接派生類中聲明為虛基類,否則仍會出現(xiàn)多次繼承。

派生類不僅要負責對直接基類進行初始化,還要負責對虛基類初始化;若多重繼承中沒有虛基類,則派生類只需要對間接基類進行初始化,而對基類的初始化由各個間接基類完成(會因此產(chǎn)生多個基類的副本保存在各個間接基類中)。

#include <iostream>
using namespace std;
class Base {
public:
	Base(int n) { nv = n; cout << "Member of Base" << endl; }
	void fun() { cout << "fun of Base" << endl; }
private:
	int nv;
};
class A:virtual public Base {	//聲明Base為虛基類,作為間接基類都需要使用virtual關鍵字
public:
	A(int a) :Base(a) { cout << "Member of A" << endl; }
private:
	int na;
};
class B :virtual public Base { //聲明Base為虛基類,作為間接基類都需要使用virtual關鍵字
public:
	B(int b) :Base(b) { cout << "Member of B" << endl; }
private:
	int nb;
};
class Derived :public A, public B {
public:
	Derived(int n) :Base(n), A(n), B(n) { cout << "Member of Derived" << endl; }
  //派生類的構(gòu)造函數(shù)初始化列表,先調(diào)用基類Base的構(gòu)造函數(shù),再依次調(diào)用間接基類A、B的構(gòu)造函數(shù)
  //由于虛基類Base中沒有默認構(gòu)造函數(shù)(允許無參構(gòu)造),所以從Base類繼承的所有派生類的構(gòu)造函數(shù)初始化表中都需要顯式調(diào)用基類(包括間接基類)的構(gòu)造函數(shù),完成初始化
private:
	int nd;
};
int main() {
	Derived de(3); de.fun();//不會產(chǎn)生二義性
	return 0;
}

關于虛基類的說明:

一個類在一個類族中既可以被用作虛基類,也可以被用作非虛基類;

如果虛基類中沒有默認構(gòu)造函數(shù)(或參數(shù)全部為默認參數(shù)的),則在派生類中必須顯式聲明構(gòu)造函數(shù),并在初始化列表中列出對虛基類構(gòu)造函數(shù)的調(diào)用;

在一個成員初始化列表中同時出現(xiàn)對虛基類和非虛基類構(gòu)造函數(shù)的調(diào)用時,虛基類的構(gòu)造函數(shù)先于非虛基類的構(gòu)造函數(shù)執(zhí)行。

三、虛函數(shù)

虛函數(shù)概念:被virtual關鍵字修飾的成員函數(shù),即為虛函數(shù),其作用就是實現(xiàn)多態(tài)性。

虛函數(shù)使用說明:

虛函數(shù)只能是類中的成員函數(shù),且不能是靜態(tài)的;

virtual關鍵字只能在類體中使用,即便虛函數(shù)的實現(xiàn)在類體外定義,也不能帶上virtual關鍵字;

當在派生類中定義了一個與基類虛函數(shù)同名的成員函數(shù)時,只要該函數(shù)的參數(shù)個數(shù)、類型、順序以及返回類型與基類中的完全一致,則派生類的這個成員函數(shù)無論是否使用virtual關鍵字,它都將自動成為虛函數(shù);

利用虛函數(shù),可以在基類和派生類中使用相同的函數(shù)名定義函數(shù)的不同實現(xiàn),達到「一個接口,多種方式」的目的。當基類指針或引用對虛函數(shù)進行訪問時,系統(tǒng)將根據(jù)運行時指針(或引用)所指向(或引用)的實際對象來確定調(diào)用的虛函數(shù)版本;

使用虛函數(shù)并不一定產(chǎn)生多態(tài)性,也不一定使用動態(tài)聯(lián)編。如:在調(diào)用中對虛函數(shù)使用類名限定,可以強制C++對該函數(shù)使用靜態(tài)聯(lián)編。

在派生類中,當一個指向基類成員函數(shù)的指針指向一個虛函數(shù),并且通過指向?qū)ο蟮幕愔羔槪ɑ蛞茫┰L問這個虛函數(shù)時,仍將發(fā)生多態(tài)性。

#include <iostream>
using namespace std;
class Base {
public:
	virtual void print() { cout << "Base-print" << endl; }
};
class Derived :public Base {
public:
	void print() { cout << "Derived-print" << endl; }
};
void display(Base* p, void(Base::* pf)()) {
	(p->*pf)();
}
int main() {
	Derived d; Base b;
	display(&d, &Base::print);	//輸出Derived-print
	display(&b, &Base::print);	//輸出Base-print
  return 0;
}

使用虛函數(shù),系統(tǒng)要增加一定的空間開銷存儲虛函數(shù)表,但是系統(tǒng)在進行動態(tài)聯(lián)編時的時間開銷時很少的,因此,虛函數(shù)實現(xiàn)的多態(tài)性是高效的。

虛函數(shù)實現(xiàn)多態(tài)的條件(同時滿足)

類之間的繼承關系滿足賦值兼容規(guī)則;

改寫了同名的虛函數(shù),但函數(shù)形參、返回類型要保持一致;

根據(jù)賦值兼容規(guī)則使用指針(或引用);

  • 使用基類指針(或引用)訪問虛函數(shù);
  • 把指針(或引用)作為函數(shù)參數(shù),這個函數(shù)不一定是類的成員函數(shù),可以是普通函數(shù),并且可以重載。

虛析構(gòu)函數(shù)

派生類的對象從內(nèi)存中撤銷時一般先調(diào)用派生類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。但如果用new運算符建立了派生類對象,且定義了一個基類的指針指向這個對象,那么當用delete運算符撤銷對象時,系統(tǒng)會只執(zhí)行基類的析構(gòu)函數(shù),而不執(zhí)行派生類的析構(gòu)函數(shù),因而也無法對派生類對象進行真正的撤銷清理工作。

如果希望delete關鍵字作用于基類指針時,也執(zhí)行派生類的析構(gòu)函數(shù),則需要將基類的析構(gòu)函數(shù)聲明為虛函數(shù)。

如果將基類的析構(gòu)函數(shù)聲明為虛函數(shù),則由該基類所派生的所有派生類的析構(gòu)函數(shù)也都自動成為虛函數(shù),即使派生類的析構(gòu)函數(shù)與基類的析構(gòu)函數(shù)名字不相同。

C++支持虛析構(gòu)函數(shù),但是不支持虛構(gòu)造函數(shù),即構(gòu)造函數(shù)不能聲明為虛函數(shù)!

純虛函數(shù)

許多情況下,不能在基類中為虛函數(shù)給出一個有意義的定義,這時可以將它說明為純虛函數(shù),將具體定義留給派生類去做。純虛函數(shù)的定義形式為:virtual 返回類型 函數(shù)名(形式參數(shù)列表) = 0;

包含有純虛函數(shù)的類稱為抽象類,一個抽象類只能作為基類來派生新類,因此又稱為抽象基類,抽象類不能定義對象(實體)。

四、多態(tài)性

多態(tài)的含義:指同一操作作用于不同的對象時產(chǎn)生不同的結(jié)果。

  • 重載多態(tài)——函數(shù)重載、運算符重載
  • 強制多態(tài)——也稱類型轉(zhuǎn)換
  • C++的基本數(shù)據(jù)類型之間轉(zhuǎn)換規(guī)則:char→short→int→unsigned→long→unsigned→float→double→long double
  • 可以在表達式中使用3中強制類型轉(zhuǎn)換表達式:static_cast<T>(E)或T(E)或(T)E 其中E代表運算表達式(獲得一個值),T代表一個類型標識符。強制多態(tài)使得類型檢查復雜化,尤其在允許重載的情況下,會導致無法消解的二義性。
  • 類型參數(shù)化多態(tài)——模板(函數(shù)模板、類模板)
  • 包含多態(tài)——使用虛函數(shù)

至少含有一個虛函數(shù)的類稱為多態(tài)類,虛函數(shù)使得程序能夠以動態(tài)聯(lián)編的方式達到執(zhí)行結(jié)果的多態(tài)化。這種多態(tài)使用的背景是:派生類繼承基類的所有操作,或者說,基類的操作能被用于操作派生類的對象,當基類的操作不能適應派生類時,派生類就需要重載基類的操作;其表現(xiàn)為C++允許用基類的指針接收派生類的地址或使用基類的引用綁定派生類的對象。

靜態(tài)聯(lián)編和動態(tài)聯(lián)編

聯(lián)編:將模塊或者函數(shù)合并在一起生成可執(zhí)行代碼的處理過程,同時對每個模塊或者函數(shù)分配內(nèi)存地址,并且對外部訪問也分配正確的內(nèi)存地址。

靜態(tài)聯(lián)編:在編譯階段就將函數(shù)實現(xiàn)和函數(shù)調(diào)用綁定。靜態(tài)聯(lián)編在編譯階段就必須了解所有函數(shù)的或模塊執(zhí)行所需要的信息,它對函數(shù)的選擇是基于指向?qū)ο蟮闹羔槪ɑ蛘咭茫┑念愋?。C語言中,所有的聯(lián)編都是靜態(tài)聯(lián)編,C++中一般情況下的聯(lián)編也是靜態(tài)聯(lián)編。

動態(tài)聯(lián)編:在程序運行的時候才進行函數(shù)實現(xiàn)和函數(shù)調(diào)用的綁定稱之為動態(tài)聯(lián)編(dynamic binding)

#include <iostream>
#define PI 3.14159265
using namespace std;
class Point {
public:
	Point(double x = 0, double y = 0) :x(x), y(y) {}
	double area_static() { return 0; }	//不是虛函數(shù),只會在編譯期綁定,形成靜態(tài)聯(lián)編
	virtual double area_dynamic() { return 0; } //用虛函數(shù)聲明,則編譯時只做賦值兼容的合法性檢查,而不做綁定
private:
	double x, y;
};
class Circle :public Point {
public:
	Circle(double r = 1.0) :r(r) {} //由于基類中的構(gòu)造函數(shù)非必須顯式傳參,所以系統(tǒng)會自動調(diào)用基類帶默認參數(shù)的構(gòu)造函數(shù)
	Circle(double x, double y, double r=1.0) :Point(x, y), r(r) {} //重載一個可傳坐標點、半徑值參數(shù)的構(gòu)造函數(shù)
	double area_static() { return PI * r * r; } //靜態(tài)聯(lián)編
	double area_dynamic() { return PI * r * r; } //動態(tài)聯(lián)編(仍為虛函數(shù)),為使可讀性更好,可在不缺省virutal關鍵字
private:
	double r;
};
int main() {
	Point o(2.5, 2.5); Circle c(2.5, 2.5, 1);
	Point* po = &o, * pc = &c, & y_c = c;
//下面五個全部為靜態(tài)聯(lián)編,無論指針指向的是基類還是派生類,由于指針類型為基類類型,且調(diào)用的不是虛函數(shù),則統(tǒng)一綁定為基類中的函數(shù)
	cout << "Point area =" << o.area_static() << endl;	//值為0
	cout << "Circle area=" << c.area_static() << endl;	//值為3.14159
	cout << "the o area from po:" << po->area_static() << endl; //值為0
	cout << "the c area from pc:" << pc->area_static() << endl;	//值為0
	cout << "the c area from cite y_c:" << y_c.area_static() << endl; //值為0
//下面三個為動態(tài)聯(lián)編,有指針(或引用)、虛函數(shù),則所調(diào)用的虛函數(shù)會在運行時通過vptr指針找到虛函數(shù)表	,根據(jù)指針指向的實際對象(而非指針類型)來判定調(diào)用誰的函數(shù)
	cout << "the o area from po:" << po->area_dynamic() << endl; //值為0
	cout << "the c area from pc:" << pc->area_dynamic() << endl; //值為3.14159
	cout << "the c area from cite y_c:" << y_c.area_dynamic() << endl; //值為3.14159
  //強制使用靜態(tài)聯(lián)編
  cout << "the c area calculated by Point::area_():" << pc->Point::area_dynamic() << endl; //值為0
	return 0;
}

動態(tài)聯(lián)編與虛函數(shù)

  • 當調(diào)用虛函數(shù)時,先通過vptr指針(編譯虛函數(shù)時,編譯器會為類自動生成一個指向虛函數(shù)表的vptr指針)找到虛函數(shù)表,然后再找出虛函數(shù)的真正地址,再調(diào)用它
  • 派生類能繼承基類的虛函數(shù)表,而且只要是和基類同名(參數(shù)也相同)的成員函數(shù),無論是否使用virtual聲明,它們都自動成為虛函數(shù)。如果派生類沒有改寫繼承基類的虛函數(shù),則函數(shù)指針調(diào)用基類的虛函數(shù);如果派生類改寫了基類的虛函數(shù),編譯器將重新為派生類的虛函數(shù)建立地址,函數(shù)指針會調(diào)用改寫后的虛函數(shù)。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

最新評論