最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

舉例:

#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ù)時(shí),使用類名限定
p->BaseB::fun(); //派生類指針調(diào)用基類同名函數(shù)時(shí),使用類名限定

名字支配規(guī)則

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

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

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

二、虛基類

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

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

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

#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關(guān)鍵字
public:
	A(int a) :Base(a) { cout << "Member of A" << endl; }
private:
	int na;
};
class B :virtual public Base { //聲明Base為虛基類,作為間接基類都需要使用virtual關(guān)鍵字
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中沒有默認(rèn)構(gòu)造函數(shù)(允許無參構(gòu)造),所以從Base類繼承的所有派生類的構(gòu)造函數(shù)初始化表中都需要顯式調(diào)用基類(包括間接基類)的構(gòu)造函數(shù),完成初始化
private:
	int nd;
};
int main() {
	Derived de(3); de.fun();//不會(huì)產(chǎn)生二義性
	return 0;
}

關(guān)于虛基類的說明:

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

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

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

三、虛函數(shù)

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

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

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

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

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

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

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

在派生類中,當(dāng)一個(gè)指向基類成員函數(shù)的指針指向一個(gè)虛函數(shù),并且通過指向?qū)ο蟮幕愔羔槪ɑ蛞茫┰L問這個(gè)虛函數(shù)時(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)要增加一定的空間開銷存儲(chǔ)虛函數(shù)表,但是系統(tǒng)在進(jìn)行動(dòng)態(tài)聯(lián)編時(shí)的時(shí)間開銷時(shí)很少的,因此,虛函數(shù)實(shí)現(xiàn)的多態(tài)性是高效的。

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

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

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

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

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

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

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

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

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

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

純虛函數(shù)

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

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

四、多態(tài)性

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

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

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

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

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

靜態(tài)聯(lián)編:在編譯階段就將函數(shù)實(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)編。

動(dòng)態(tài)聯(lián)編:在程序運(yùn)行的時(shí)候才進(jìn)行函數(shù)實(shí)現(xiàn)和函數(shù)調(diào)用的綁定稱之為動(dòng)態(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ù),只會(huì)在編譯期綁定,形成靜態(tài)聯(lián)編
	virtual double area_dynamic() { return 0; } //用虛函數(shù)聲明,則編譯時(shí)只做賦值兼容的合法性檢查,而不做綁定
private:
	double x, y;
};
class Circle :public Point {
public:
	Circle(double r = 1.0) :r(r) {} //由于基類中的構(gòu)造函數(shù)非必須顯式傳參,所以系統(tǒng)會(huì)自動(dòng)調(diào)用基類帶默認(rèn)參數(shù)的構(gòu)造函數(shù)
	Circle(double x, double y, double r=1.0) :Point(x, y), r(r) {} //重載一個(gè)可傳坐標(biāo)點(diǎn)、半徑值參數(shù)的構(gòu)造函數(shù)
	double area_static() { return PI * r * r; } //靜態(tài)聯(lián)編
	double area_dynamic() { return PI * r * r; } //動(dòng)態(tài)聯(lián)編(仍為虛函數(shù)),為使可讀性更好,可在不缺省virutal關(guān)鍵字
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;
//下面五個(gè)全部為靜態(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
//下面三個(gè)為動(dòng)態(tài)聯(lián)編,有指針(或引用)、虛函數(shù),則所調(diào)用的虛函數(shù)會(huì)在運(yùn)行時(shí)通過vptr指針找到虛函數(shù)表	,根據(jù)指針指向的實(shí)際對象(而非指針類型)來判定調(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
  //強(qiáng)制使用靜態(tài)聯(lián)編
  cout << "the c area calculated by Point::area_():" << pc->Point::area_dynamic() << endl; //值為0
	return 0;
}

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

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

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

相關(guān)文章

最新評論

贵南县| 阜城县| 钦州市| 龙井市| 伊金霍洛旗| 修水县| 高陵县| 朝阳区| 白玉县| 临泽县| 蛟河市| 类乌齐县| 象山县| 株洲市| 无锡市| 湘阴县| 临高县| 定陶县| 招远市| 五河县| 冕宁县| 怀宁县| 乌拉特中旗| 崇左市| 新宁县| 莱州市| 大兴区| 仙游县| 河源市| 乐都县| 恩平市| 栾城县| 太康县| 龙陵县| 垣曲县| 汉源县| 札达县| 万荣县| 邯郸县| 蒲城县| 伊川县|