欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

C++知識(shí)點(diǎn)--多態(tài)-創(chuàng)新互聯(lián)

C++知識(shí)點(diǎn) – 多態(tài)

河西ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為成都創(chuàng)新互聯(lián)的ssl證書(shū)銷(xiāo)售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書(shū)合作)期待與您的合作!文章目錄
  • C++知識(shí)點(diǎn) -- 多態(tài)
  • 一、多態(tài)概念
    • 1.概念
  • 二、多態(tài)的定義及實(shí)現(xiàn)
    • 1、多態(tài)的構(gòu)成條件
    • 2、虛函數(shù)
    • 3、虛函數(shù)的重寫(xiě)
    • 4.虛函數(shù)重寫(xiě)的特例
    • 5、不符合多態(tài)的場(chǎng)景
    • 6、C++11的override和final
    • 7、重載、覆蓋(重寫(xiě))、隱藏(重定義)的對(duì)比
    • 8、例題
  • 三、抽象類(lèi)
  • 四、多態(tài)的原理
    • 1、虛函數(shù)表
    • 2、多態(tài)的原理
  • 五、單繼承和多繼承關(guān)系的虛函數(shù)表
    • 1、單繼承中的虛表
    • 2、多繼承中的虛表
  • 六、多態(tài)常見(jiàn)面試問(wèn)題
    • 1.例題
    • 2.inline函數(shù)可以是虛函數(shù)嗎
    • 3.靜態(tài)成員函數(shù)可以是虛函數(shù)嗎
    • 4.構(gòu)造函數(shù)可以是虛函數(shù)嗎
    • 5.析構(gòu)函數(shù)可以是虛函數(shù)嗎
    • 6.拷貝構(gòu)造和賦值可以是虛函數(shù)嗎
    • 7.對(duì)象訪問(wèn)普通函數(shù)快還是虛函數(shù)快
    • 8.虛函數(shù)表是在什么階段生成的,存在哪里


一、多態(tài)概念 1.概念

多態(tài)就是完成某個(gè)行為時(shí),不同對(duì)象去完成時(shí)會(huì)產(chǎn)生不同的狀態(tài)。
比如買(mǎi)票,普通人買(mǎi)全價(jià)票,學(xué)生買(mǎi)半價(jià)票,軍人優(yōu)先買(mǎi)票。

二、多態(tài)的定義及實(shí)現(xiàn) 1、多態(tài)的構(gòu)成條件

多態(tài)是在不同繼承關(guān)系的類(lèi)對(duì)象,去調(diào)用同一函數(shù),產(chǎn)生不同的行為。
繼承中構(gòu)成多態(tài)還有兩個(gè)條件:
1.必須通過(guò)基類(lèi)的指針或引用去調(diào)用虛函數(shù);
2.被調(diào)用的函數(shù)必須是虛函數(shù),且派生類(lèi)必須對(duì)基類(lèi)的虛函數(shù)進(jìn)行重寫(xiě);

2、虛函數(shù)

用virtual關(guān)鍵字修飾的成員函數(shù)就是虛函數(shù)。
代碼如下:

class Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};
3、虛函數(shù)的重寫(xiě)

派生類(lèi)中的虛函數(shù)構(gòu)成重寫(xiě)(覆蓋)的條件有:函數(shù)名、參數(shù)和返回值類(lèi)型相同,但是函數(shù)的實(shí)現(xiàn)不同;
如果不構(gòu)成重寫(xiě),就是隱藏關(guān)系;
代碼如下:

class Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;      //重寫(xiě)
	}
};

class Soldier : public Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 優(yōu)先"<< endl;      //重寫(xiě)
	}
};

void Func(Person& p)           //使用父類(lèi)的引用調(diào)用虛函數(shù)
{p.BuyTicket();
}

void Test()
{Person p;
	Func(p);

	Student st;
	Func(st);

	Soldier sd;
	Func(sd);
}

以上代碼就完整的構(gòu)成了多態(tài),其運(yùn)行效果為:
在這里插入圖片描述
不同的對(duì)象調(diào)用同一個(gè)虛函數(shù),呈現(xiàn)出了不同的效果。

4.虛函數(shù)重寫(xiě)的特例

1.將子類(lèi)中虛函數(shù)的virtual去掉

class Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	void BuyTicket()
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;
	}
};

這樣依然構(gòu)成重寫(xiě),子類(lèi)中依然是虛函數(shù),編譯器認(rèn)為先把父類(lèi)的虛函數(shù)繼承下來(lái)了,而且是接口繼承,將函數(shù)接口完整繼承下來(lái)了,子類(lèi)中只是將函數(shù)的實(shí)現(xiàn)進(jìn)行重寫(xiě)。

2.重寫(xiě)的協(xié)變
返回值類(lèi)型可以不同,要求必須是父子關(guān)系的指針或引用;
代碼如下:

class Person
{public:
	virtual Person* BuyTicket()
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;
	}
};

以上的代碼是會(huì)報(bào)錯(cuò)的,因?yàn)橹粷M足了返回值類(lèi)型不同,并不是父子關(guān)系的指針或引用,下面的代碼才是協(xié)變:

class Person
{public:
	virtual Person* BuyTicket()
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
		return this;
	}
};

class Student : public Person
{public:
	virtual Student* BuyTicket()       //返回值是有父子關(guān)系的指針或引用
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;
		return this;
	}
};

運(yùn)行結(jié)果為:
在這里插入圖片描述
上面的代碼依然能夠構(gòu)成多態(tài)。

3.析構(gòu)函數(shù)的重寫(xiě)
建議在繼承中將析構(gòu)函數(shù)定義為虛函數(shù);

class Person
{public:
	virtual ~Person()
	{cout<< "~Person()"<< endl;
	}
};

class Student : public Person
{public:
	virtual ~Student()      //子類(lèi)的析構(gòu)函數(shù)與父類(lèi)的析構(gòu)函數(shù)的函數(shù)名并不相同
	{cout<< "~Student()"<< endl;
	}
};
int main()
{Person* p1 = new Person;
	delete p1;

	Person* p2 = new Student;//父類(lèi)指針指向子類(lèi)對(duì)象,符合多態(tài)調(diào)用
	delete p2;
	
	return 0;
}

以上代碼的運(yùn)行結(jié)果為:
在這里插入圖片描述
子類(lèi)和父類(lèi)的析構(gòu)函數(shù),參數(shù)類(lèi)型和返回值類(lèi)型都相同,編譯器為了讓他們構(gòu)成重寫(xiě),將析構(gòu)函數(shù)名改寫(xiě)為destructor,所以,上述代碼中析構(gòu)函數(shù)完成了重寫(xiě)。
只有子類(lèi)析構(gòu)函數(shù)重寫(xiě)了父類(lèi)的析構(gòu)函數(shù),這里才能正確調(diào)用,指針指向父類(lèi)對(duì)象,調(diào)用父類(lèi)的析構(gòu)函數(shù),指向子類(lèi)對(duì)象就調(diào)用子類(lèi)的析構(gòu)函數(shù)。

如果析構(gòu)函數(shù)不是虛函數(shù):

class Person
{public:
	~Person()
	{cout<< "~Person()"<< endl;
	}
};

class Student : public Person
{public:
	~Student()      //子類(lèi)的析構(gòu)函數(shù)與父類(lèi)的析構(gòu)函數(shù)的函數(shù)名并不相同
	{cout<< "~Student()"<< endl;
	}
};
int main()
{Person* ptr1 = new Person;
	delete ptr1;

	Person* ptr2 = new Student;//父類(lèi)指針指向子類(lèi)對(duì)象,符合多態(tài)調(diào)用
	delete ptr2;
	
	return 0;
}

在這里插入圖片描述
在子類(lèi)delete時(shí),調(diào)用的還是父類(lèi)的析構(gòu)函數(shù):
在這里插入圖片描述
這里是普通調(diào)用,不符合多態(tài),在編譯時(shí)就決定了;
ptr2是Person*類(lèi)型的指針,call的是Person的析構(gòu)函數(shù);
ptr1希望調(diào)用父類(lèi)的析構(gòu),ptr2希望調(diào)用子類(lèi)的析構(gòu),所以把析構(gòu)設(shè)計(jì)成符合多態(tài)的函數(shù)名。

5、不符合多態(tài)的場(chǎng)景

1.不是父類(lèi)的指針或引用調(diào)用虛函數(shù)
代碼如下:

class Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;
	}
};

void Func(Person p)
{p.BuyTicket();
}

運(yùn)行結(jié)果為:
在這里插入圖片描述
上述代碼是不構(gòu)成多態(tài)的。

2.不符合虛函數(shù)重寫(xiě)
2.1將父類(lèi)虛函數(shù)的virtual去掉

class Person
{public:
	void BuyTicket()
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;
	}
};

void Func(Person& p)
{p.BuyTicket();
}

運(yùn)行結(jié)果為:
在這里插入圖片描述
是不符合虛函數(shù)重寫(xiě)的,自然就不構(gòu)成多態(tài)。

2.2參數(shù)類(lèi)型不同

class Person
{public:
	void BuyTicket(char)
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	virtual void BuyTicket(int)
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;
	}
};

void Func(Person& p)
{p.BuyTicket();
}

運(yùn)行結(jié)果為:
在這里插入圖片描述
同樣不符合多態(tài)。

6、C++11的override和final

1.final:修飾虛函數(shù),表示其不能再被重寫(xiě)(用的很少)

class Person
{public:
	virtual void BuyTicket() final
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	virtual void BuyTicket()
	{cout<< "買(mǎi)票 - 半價(jià)"<< endl;
	}
};

編譯之后會(huì)報(bào)錯(cuò):
在這里插入圖片描述
2.override:檢查派生類(lèi)虛函數(shù)是否重寫(xiě)了某個(gè)基類(lèi)的虛函數(shù),若沒(méi)有重寫(xiě)編譯報(bào)錯(cuò)(常用)

class Person
{public:
	virtual void BuyTicket(int)
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

class Student : public Person
{public:
	virtual void BuyTicket(char) override//參數(shù)類(lèi)型不一致,未完成重寫(xiě)
	{cout<< "買(mǎi)票 - 全價(jià)"<< endl;
	}
};

上述代碼子類(lèi)的虛函數(shù)未完成重寫(xiě),在后面加了override后,編譯器就會(huì)報(bào)錯(cuò):
在這里插入圖片描述
override常用于檢查子類(lèi)虛函數(shù)重寫(xiě)的語(yǔ)法是否正確。

7、重載、覆蓋(重寫(xiě))、隱藏(重定義)的對(duì)比

在這里插入圖片描述

8、例題

代碼如下:

#includeusing namespace std;

class A
{public:
	virtual void func(int val = 1)
	{cout<< "A ->"<< val<< endl;
	}

	virtual void test()
	{func();
	}
};

class B : public A
{public:
	virtual void func(int val = 0)
	{cout<< "B ->"<< val<< endl;
	}
};

int main()
{B* p = new B;
	p->test();

	return 0;
}

以上代碼的輸出結(jié)果為:
在這里插入圖片描述
分析:

  1. A為父類(lèi),B公有繼承A,繼承了A的func和test函數(shù),其中A和B的func函數(shù)構(gòu)成了虛函數(shù)重寫(xiě)(不要求參數(shù)的缺省值相同),因此構(gòu)成了多態(tài);
  2. main函數(shù)中,B指針p指向B對(duì)象,用p調(diào)用了test函數(shù),p的類(lèi)型是B,而test中this指針的類(lèi)型是A*,p傳給this,這里用父類(lèi)指針指向子類(lèi)對(duì)象,構(gòu)成了切片;
  3. 這里this指針是A*類(lèi)型的,用this調(diào)用func函數(shù),符合父類(lèi)指針調(diào)用虛函數(shù),符合多態(tài)調(diào)用,多態(tài)調(diào)用時(shí),指針指向那個(gè)類(lèi)對(duì)象,就調(diào)用哪個(gè)類(lèi)中的虛函數(shù),顯然p和this指向的都是子類(lèi)對(duì)象,所以這里調(diào)用的是子類(lèi)中的虛函數(shù);
  4. 虛函數(shù)是接口繼承,普通函數(shù)數(shù)實(shí)現(xiàn)繼承;虛函數(shù)繼承時(shí),直接將父類(lèi)的函數(shù)接口繼承下來(lái),與子類(lèi)的接口是無(wú)關(guān)的,子類(lèi)重寫(xiě)的是實(shí)現(xiàn),這里的接口是父類(lèi)的接口,val的缺省值是1,因此函數(shù)最終的輸出結(jié)果為:B ->1,選B。

如果將代碼改成以下形式:

class A
{public:
	virtual void func(int val)//去掉缺省值
	{cout<< "A ->"<< val<< endl;
	}

	virtual void test()
	{func(1);
	}
};

class B : public A
{public:
	void func(int val)
	{cout<< "B ->"<< val<< endl;
	}
};

int main()
{//Test();

	A* p = new B;//用父類(lèi)的指針指向子類(lèi)對(duì)象
	p->test();

	return 0;
}

子類(lèi)和父類(lèi)的func依然構(gòu)成虛函數(shù)重寫(xiě);
這里用父類(lèi)的指針指向子類(lèi)對(duì)象,發(fā)生了切片,但指向的還是子類(lèi)的對(duì)象,所以調(diào)用的函數(shù)還是子類(lèi)中的虛函數(shù),結(jié)果還是:B ->1;
在這里插入圖片描述
最終結(jié)果與p的指針類(lèi)型無(wú)關(guān),只與它指向的對(duì)象有關(guān)。

三、抽象類(lèi)

在虛函數(shù)的后面寫(xiě)上 = 0,這個(gè)函數(shù)就是純虛函數(shù),包含純虛函數(shù)的類(lèi)叫做抽象類(lèi)(接口類(lèi)),抽象類(lèi)不能實(shí)例化出對(duì)象,派生類(lèi)繼承抽象類(lèi)后也不能實(shí)例化出對(duì)象,只有派生類(lèi)重寫(xiě)了虛函數(shù),才能實(shí)例化對(duì)象,純虛函數(shù)規(guī)范了派生類(lèi)必須重寫(xiě),更好的體現(xiàn)出了接口繼承。
代碼如下:

class Car             //把不想實(shí)例化出對(duì)象的父類(lèi)定義為抽象類(lèi)
{public:
	virtual void Drive() = 0;
};

class Benz : public Car
{public:
	virtual void Drive(int)     //如果子類(lèi)繼承了抽象類(lèi)卻未完成虛函數(shù)重寫(xiě),就會(huì)報(bào)錯(cuò)
	{cout<< "Benz - 舒適"<< endl;
	}
};

class BMW : public Car
{public:
	virtual void Drive()
	{cout<< "BMW - 操控"<< endl;
	}
};

int main()
{Car c1;
	Benz c2;
	BMW c3;

	return 0;
}

在這里插入圖片描述
1.抽象類(lèi)一般用于定義接口,將不想實(shí)例化出對(duì)象的類(lèi)定義為抽象類(lèi);
2.抽象函數(shù)強(qiáng)制子類(lèi)完成虛函數(shù)的重寫(xiě),不重寫(xiě)就無(wú)法實(shí)例化,而override是檢查語(yǔ)法是否完成重寫(xiě);

四、多態(tài)的原理 1、虛函數(shù)表

創(chuàng)建如下對(duì)象:

class Base
{public:
	virtual void func()
	{cout<< "func"<< endl;
	}
private:
	int _b = 0;
};

int main()
{Base b;
	cout<< sizeof(b)<< endl;

	return 0;
}

我么可以發(fā)現(xiàn)sizeof(b)的結(jié)果是8,再看b對(duì)象實(shí)例化后的成員
在這里插入圖片描述
可以發(fā)現(xiàn)在成員_b的上面還有一個(gè)_vfptr的成員,這叫做虛函數(shù)表指針;帶有虛函數(shù)的類(lèi)對(duì)象,其成員中都有一個(gè)虛函數(shù)表指針,因?yàn)檫x虛函數(shù)要放到虛函數(shù)表中,也簡(jiǎn)稱虛表。
將Base繼承給子類(lèi),代碼如下:

class Base
{public:
	virtual void func1()
	{cout<< "Base::func1"<< endl;
	}
	virtual void func2()             //加一個(gè)虛函數(shù)func2
	{cout<< "Base::func2"<< endl;
	}
	void func3()                     //加一個(gè)普通函數(shù)func3
	{cout<< "Base::func3"<< endl;
	}

private:
	int _b = 1;
};

class Derive : public Base
{public:
	virtual void func1()                //重寫(xiě)父類(lèi)虛函數(shù)
	{cout<< "Derive::func1"<< endl;
	}
private:
	int _d = 2;
};

int main()
{Base b;
	cout<< sizeof(b)<< endl;

	Derive d;

	return 0;
}

通過(guò)監(jiān)視窗口我們可以看到:
在這里插入圖片描述
1.子類(lèi)對(duì)象d中也有一個(gè)虛函數(shù)表指針,且和父類(lèi)對(duì)象b的虛表指針不同,由于子類(lèi)對(duì)func1完成了重寫(xiě),虛表中的func1就是子類(lèi)重寫(xiě)后的Detive::func1;
2.func2是虛函數(shù),繼承下來(lái)也會(huì)放進(jìn)子類(lèi)的虛表,而func3不是虛函數(shù),不會(huì)放進(jìn)虛表;
3.虛表本身是一個(gè)放函數(shù)指針的數(shù)組,一般情況最后會(huì)放一個(gè)nullptr(vs環(huán)境下);
4.虛表存放的是虛函數(shù)的函數(shù)指針,不是虛函數(shù),虛函數(shù)跟普通函數(shù)一樣,都存放在代碼段。

2、多態(tài)的原理

在這里插入圖片描述
在這里插入圖片描述
通過(guò)對(duì)匯編代碼的分析,我們可以總結(jié)出:
1.滿足多態(tài)以后的函數(shù)調(diào)用,不是在編譯時(shí)確定的,是運(yùn)行起來(lái)以后再到對(duì)象中找的,程序運(yùn)行時(shí)取對(duì)象中的虛表指針找到函數(shù)地址,再去調(diào)用;
2.普通函數(shù)的調(diào)用,是在編譯鏈接時(shí)就確定函數(shù)的地址,運(yùn)行時(shí)直接調(diào)用。

五、單繼承和多繼承關(guān)系的虛函數(shù)表 1、單繼承中的虛表

代碼如下:

class Base
{public:
	virtual void func1()
	{cout<< "Base::func1"<< endl;
	}
	virtual void func2()
	{cout<< "Base::func2"<< endl;
	}

private:
	int _b = 1;
};

class Derive : public Base
{public:
	virtual void func1()
	{cout<< "Derive::func1"<< endl;
	}
	virtual void func3()
	{cout<< "Derive::func3"<< endl;
	}
	virtual void func4()
	{cout<< "Derive::func4"<< endl;
	}

private:
	int _d = 2;
};

在這里插入圖片描述
通過(guò)監(jiān)視窗口看不見(jiàn)func3和func4,我們可以使用代碼打印虛表中的函數(shù):

class Base
{public:
	virtual void func1()
	{cout<< "Base::func1"<< endl;
	}
	virtual void func2()
	{cout<< "Base::func2"<< endl;
	}

private:
	int _b = 1;
};

class Derive : public Base
{public:
	virtual void func1()
	{cout<< "Derive::func1"<< endl;
	}
	virtual void func3()
	{cout<< "Derive::func3"<< endl;
	}
	virtual void func4()
	{cout<< "Derive::func4"<< endl;
	}

private:
	int _d = 2;
};

typedef void(*VFPTR) ();     //將指向返回值為void、沒(méi)有參數(shù)的類(lèi)型的函數(shù)的指針重定義為VFPTR
void PrintVTable(VFPTR vTable[])
{//依次取虛表中的指針打印并調(diào)用,調(diào)用就可以看出存的是哪個(gè)函數(shù)
	cout<< "虛表地址>"<< vTable<< endl;
	for (int i = 0; vTable[i] != nullptr; i++)
	{printf("第%d個(gè)虛函數(shù)地址:0x%x", i, vTable[i]);//打印地址
		VFPTR f = vTable[i];//用函數(shù)指針取出虛函數(shù)地址
		f();//調(diào)用
	}
	cout<< endl;
}

int main()
{Base b1;
	Base b2;
	Derive d;

	VFPTR* vTableb1 = (VFPTR*)(*((int*)&b1));//將b對(duì)象的地址取出,強(qiáng)轉(zhuǎn)成int*,再解引用,就取出了b的頭四個(gè)字節(jié)的數(shù)據(jù),這個(gè)就是指向虛表的指針
										   //再?gòu)?qiáng)轉(zhuǎn)成VFPTR*,因?yàn)樘摫砭褪荲FPTR類(lèi)型的數(shù)組
	PrintVTable(vTableb1);

	VFPTR* vTableb2 = (VFPTR*)(*((int*)&b2));
	PrintVTable(vTableb2);


	VFPTR* vTabled = (VFPTR*)(*((int*)&d));
	PrintVTable(vTabled);

	return 0;
}

在這里插入圖片描述
在這里插入圖片描述
我么可以看出,在vs下:
1.同一個(gè)類(lèi)型的對(duì)象,共用一個(gè)虛表(b1和b2);
2.不管是否完成重寫(xiě)名子類(lèi)虛表和父類(lèi)虛表都不是同一個(gè);
3.單繼承中,子類(lèi)的所有虛函數(shù),包括重寫(xiě)父類(lèi)的虛函數(shù)和未重寫(xiě)的虛函數(shù),都放在同一個(gè)虛表中。

2、多繼承中的虛表

代碼如下:

class Base1
{public:
	virtual void func1()
	{cout<< "Base1::func1"<< endl;
	}
	virtual void func2()
	{cout<< "Base1::func2"<< endl;
	}

private:
	int _b1 = 1;
};

class Base2
{public:
	virtual void func1()
	{cout<< "Base2::func1"<< endl;
	}
	virtual void func2()
	{cout<< "Base2::func2"<< endl;
	}

private:
	int _b2 = 2;
};


class Derive : public Base1, public Base2
{public:
	virtual void func1()
	{cout<< "Derive::func1"<< endl;
	}
	virtual void func3()
	{cout<< "Derive::func3"<< endl;
	}

private:
	int _d = 3;
};

typedef void(*VFPTR) ();     //將指向返回值為void、沒(méi)有參數(shù)的類(lèi)型的函數(shù)的指針重定義為VFPTR
void PrintVTable(VFPTR vTable[])
{//依次取虛表中的指針打印并調(diào)用,調(diào)用就可以看出存的是哪個(gè)函數(shù)
	cout<< "虛表地址>"<< vTable<< endl;
	for (int i = 0; vTable[i] != nullptr; i++)
	{printf("第%d個(gè)虛函數(shù)地址:0x%x", i, vTable[i]);//打印地址
		VFPTR f = vTable[i];//用函數(shù)指針取出虛函數(shù)地址
		f();//調(diào)用
	}
	cout<< endl;
}

int main()
{Derive d;

	VFPTR* vTabled1 = (VFPTR*)(*((int*)&d));
	PrintVTable(vTabled1);

	VFPTR* vTabled2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));//從Base2的虛表中取虛函數(shù)地址
	PrintVTable(vTabled2);


	return 0;
}

Derive多繼承Base1和Base2,其中Derive重寫(xiě)了func1,而func1既是Base1的虛函數(shù),也是Base2的虛函數(shù),func3是Derive自己的虛函數(shù),運(yùn)行結(jié)果如下:
在這里插入圖片描述
在這里插入圖片描述

可以看出在多繼承下:
1.子類(lèi)中每一個(gè)繼承的父類(lèi)都有自己的虛表,存放父類(lèi)中的虛函數(shù);
2.子類(lèi)中重寫(xiě)的虛函數(shù)會(huì)覆蓋子類(lèi)中父類(lèi)虛表對(duì)應(yīng)的虛函數(shù),Base1和Base2中的func1都沒(méi)覆蓋為了Derive::func1;
3.子類(lèi)中繼承的Base1中的func1和Base2中的func1的地址不同,但它們都是Derive重寫(xiě)后的虛函數(shù),最終調(diào)用的是同一個(gè)func1,只是中間多了一個(gè)步驟;
4.子類(lèi)未重寫(xiě)的的虛函數(shù)放在第一個(gè)繼承的父類(lèi)的虛表中;
在這里插入圖片描述

六、多態(tài)常見(jiàn)面試問(wèn)題 1.例題

以下程序的輸出結(jié)果是:
在這里插入圖片描述
B和C都是虛繼承A,D多繼承B和C,所以B和C在D中共享一個(gè)A,所以B和C都不能去初始化D中的A對(duì)象,只能在D中單獨(dú)進(jìn)行A的初始化;
初始化是按照類(lèi)聲明的順序來(lái)的,不是按照初始化列表的順序,所以在D中先初始化A對(duì)象,在初始化B和C,這事就不會(huì)重復(fù)初始化A了,最后初始化D,所以答案選A。

2.inline函數(shù)可以是虛函數(shù)嗎

可以,inline函數(shù)是沒(méi)有地址的,而且inline只是對(duì)編譯器的一個(gè)建議,當(dāng)一個(gè)inline函數(shù)是虛函數(shù)時(shí),在多態(tài)調(diào)用以后,inline就失效了,因?yàn)樘摵瘮?shù)要放進(jìn)虛表中。

3.靜態(tài)成員函數(shù)可以是虛函數(shù)嗎

不可以,static函數(shù)沒(méi)有this指針,可以直接使用類(lèi)名::函數(shù)名()的方式調(diào)用,而使用類(lèi)名::函數(shù)名()的方式無(wú)法訪問(wèn)對(duì)象的虛表,因此靜態(tài)成員函數(shù)無(wú)法放進(jìn)虛表,虛函數(shù)是為了實(shí)現(xiàn)多態(tài),多態(tài)運(yùn)行時(shí)都是去虛表中找決議,靜態(tài)成員函數(shù)都是在編譯時(shí)就決議了,因此它是虛函數(shù)沒(méi)有價(jià)值。

4.構(gòu)造函數(shù)可以是虛函數(shù)嗎

不可以,因?yàn)樘摵瘮?shù)是為了實(shí)現(xiàn)多態(tài)調(diào)用,運(yùn)行時(shí)去虛表中找對(duì)應(yīng)的虛函數(shù)進(jìn)行調(diào)用,對(duì)象中的虛表指針都是在構(gòu)造函數(shù)初始化列表階段才初始化的,構(gòu)造函數(shù)是虛函數(shù)沒(méi)有意義。

5.析構(gòu)函數(shù)可以是虛函數(shù)嗎

可以,并且最好把基類(lèi)的析構(gòu)函數(shù)定義為虛函數(shù),詳情參考 二-5-3。

6.拷貝構(gòu)造和賦值可以是虛函數(shù)嗎

拷貝構(gòu)造不可以,因?yàn)榭截悩?gòu)造也是構(gòu)造函數(shù),參考上面的構(gòu)造函數(shù);
賦值重載operator==()可以,但是沒(méi)有實(shí)際價(jià)值。

7.對(duì)象訪問(wèn)普通函數(shù)快還是虛函數(shù)快

如果虛函數(shù)不構(gòu)成多態(tài),是一樣快的;
如果虛函數(shù)構(gòu)成多態(tài),調(diào)用普通函數(shù)比較快,因?yàn)闃?gòu)成多態(tài)調(diào)用虛函數(shù)時(shí),運(yùn)行中需要到虛表中去查找。

8.虛函數(shù)表是在什么階段生成的,存在哪里

虛函數(shù)表是在編譯階段就生成好的,存在代碼段(常量區(qū));
構(gòu)造函數(shù)初始化列表階段初始化的是虛函數(shù)表指針,對(duì)象中存的也是虛函數(shù)表指針。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

當(dāng)前名稱:C++知識(shí)點(diǎn)--多態(tài)-創(chuàng)新互聯(lián)
鏈接地址:http://aaarwkj.com/article10/ddoego.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、虛擬主機(jī)關(guān)鍵詞優(yōu)化、軟件開(kāi)發(fā)外貿(mào)網(wǎng)站建設(shè)、品牌網(wǎng)站設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開(kāi)發(fā)
国产激情福利一区二区| 亚洲av毛片一区二区三区网| 蜜桃精品一区二区三区在线观看| 国产精品一区二区污网站| 亚洲国产精品二区三区| 全国最大成人免费视频| 日韩欧美黄片一区二区三区| 女厕所偷拍一区二区三区| 日本91大神在线观看| 欧美精品一区二区三区在线| 中文字幕成人乱码亚洲| 国产精品亚洲av在线| 国产男女乱淫一区二区三区| 欧美日韩精品视频在线| 欧美日韩免费高清视视频| 美女在线视频一区二区三区| 麻豆精品情欲人妻一区| 色噜噜噜av天堂九区| 国产超碰久久久久久精品| 少妇38p高潮在线| 亚洲午夜天堂精品福利天堂| 国产亚洲一区二区视频| 插美女逼免费视频导航| 日本区一区二区三高清视频| 亚洲一区二区三区免费观看视频| 欧美日韩中文国产天堂| 日本免费观看一区久久| 大胸妇女引诱老师在线观看| 欧美+亚洲+精品+三区| 国产男女猛烈无遮挡网站| 日本午夜精品在线观看| 日韩精品综合成人欧美| 日本东京热二三四区不卡免费的| 欧美国产一级二级三级| 日韩精品国产自拍在线| 欧美口爆吞精在线播放| 国产精品对白久久久久粗| 日本啪啪啪一区二区三区| 中文字幕乱码日韩一二三区| 欧美日韩在线一区二区精品| 亚洲欧洲另类美女久久精品|