.net網(wǎng)站開(kāi)發(fā)的例子抖音優(yōu)化
一、多態(tài)的概念及定義
1.1 多態(tài)的概念
多態(tài)簡(jiǎn)單來(lái)說(shuō)就是多種形態(tài)
同一個(gè)行為,不同對(duì)象去完成時(shí)
會(huì)產(chǎn)生出不同的狀態(tài)
多態(tài)分為靜態(tài)多態(tài)
和動(dòng)態(tài)多態(tài)
靜態(tài)多態(tài)指的是編譯時(shí)
在程序編譯期間確定了程序的行為
比如:函數(shù)重載
動(dòng)態(tài)多態(tài)指的是運(yùn)行時(shí)
在程序運(yùn)行期間,根據(jù)具體拿到的類(lèi)型
確定程序的具體行為,調(diào)用具體的函數(shù)
1.2 在繼承中要構(gòu)成多態(tài)的兩個(gè)條件
-
必須通過(guò)父類(lèi)指針或引用調(diào)用虛函數(shù)
-
虛函數(shù)的重寫(xiě)
函數(shù)名、參數(shù)類(lèi)型、返回值都要相同
被virtual修飾的類(lèi)成員函數(shù)稱(chēng)為虛函數(shù)
class Person {
public:virtual void BuyTicket() { cout << "買(mǎi)票-全價(jià)" << endl;}
};
1.3 虛函數(shù)的重寫(xiě)(覆蓋)
派生類(lèi)中有一個(gè)跟基類(lèi)完全相同的虛函數(shù)
(即派生類(lèi)虛函數(shù)與基類(lèi)虛函數(shù)的返回值類(lèi)
型、函數(shù)名字、參數(shù)列表完全相同)
稱(chēng)子類(lèi)的虛函數(shù)重寫(xiě)了基類(lèi)的虛函數(shù)
普通函數(shù)的繼承是實(shí)現(xiàn)繼承
派生類(lèi)繼承了基類(lèi)函數(shù),可以使用函數(shù)
繼承的是函數(shù)的實(shí)現(xiàn)
虛函數(shù)的繼承是接口繼承
派生類(lèi)繼承的是基類(lèi)虛函數(shù)的接口
目的是為了重寫(xiě),達(dá)成多態(tài),繼承的是接口
如果不實(shí)現(xiàn)多態(tài),不要把函數(shù)定義成虛函數(shù)
class Person {
public:virtual void BuyTicket() { cout << "買(mǎi)票-全價(jià)" << endl; }// 只要父類(lèi)析構(gòu)加了virtual就構(gòu)成多態(tài),子類(lèi)加不加都可以正確釋放virtual ~Person() { cout << "~Person" << endl; };
};class Student : public Person {
public: // 子類(lèi)可以不寫(xiě)virtual,因?yàn)樗^承父類(lèi)的接口,重寫(xiě)實(shí)現(xiàn)virtual void BuyTicket() { cout << "買(mǎi)票-半價(jià)" << endl; }~Student() { cout << "~Student" << endl; }
};void Func(Person& p)
{ p.BuyTicket(); }int main()
{
Person ps;
Student st;// 構(gòu)成多態(tài)后
Func(ps); // 傳父類(lèi)調(diào)用父類(lèi)的虛函數(shù)
Func(st); // 傳子類(lèi)調(diào)用子類(lèi)的虛函數(shù)return 0;
}
1.4 協(xié)變
如果是父子關(guān)系的指針或引用
返回值可以不同也構(gòu)成多態(tài)
class A{};
class B : public A {};
class Person {
public:virtual A* f() {return new A;}
};
class Student : public Person {
public:virtual B* f() {return new B;}
};
1.5 final和override
final: 修飾虛函數(shù)
表示該虛函數(shù)不能再被重寫(xiě)
現(xiàn)實(shí)中不常用,不能實(shí)現(xiàn)多態(tài)的虛函數(shù)
意義不大
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() {cout << "Benz-舒適" << endl;}
};
override: 檢查派生類(lèi)虛函數(shù)
是否重寫(xiě)了基類(lèi)某個(gè)虛函數(shù)
如果沒(méi)有重寫(xiě)編譯報(bào)錯(cuò)
class Car{
public:virtual void Drive(){}
};
class Benz :public Car {
public:virtual void Drive() override {cout << "Benz-舒適" << endl;}
};
1.6 重載、覆蓋(重寫(xiě))、隱藏(重定義)的對(duì)比
面試題經(jīng)常被問(wèn)到
1.7 抽象類(lèi)
在虛函數(shù)后面加上 =0
這個(gè)函數(shù)就叫純虛函數(shù)
包含純虛函數(shù)的類(lèi)叫做抽象類(lèi)
抽象類(lèi)不能實(shí)例化出對(duì)象
派生類(lèi)繼承后也不能實(shí)例化出對(duì)象
只有重寫(xiě)純虛函數(shù),派生類(lèi)才能實(shí)例化出對(duì)象
class Car
{
public:// 純虛函數(shù) --- 抽象類(lèi)virtual void Drive() = 0;
};int main()
{Car car; // 無(wú)法實(shí)例化對(duì)象return 0;
}
二、多態(tài)的原理
2.1 虛函數(shù)表
這里??家坏拦P試題:sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};int main()
{cout << sizeof(Base) << endl;return 0;
}
在32位操作系統(tǒng)下是8 bit
在64位操作系統(tǒng)下是16 bit
通過(guò)調(diào)試發(fā)現(xiàn)還有個(gè)指針_vfptr
這個(gè)指針叫做虛函數(shù)表指針
本質(zhì)是指針數(shù)組
用來(lái)存放虛函數(shù)的地址
對(duì)象中存的是虛表指針
虛表存的是虛函數(shù)指針
虛函數(shù)和普通函數(shù)一樣的
都是存在代碼段的
2.2 多態(tài)的原理
通過(guò)下面代碼觀察父子類(lèi)
的虛表之間的關(guān)系
class Base
{
public:virtual void Func(){cout << "Base::Func1()" << endl;}
};
class Derive : public Base
{
public:virtual void Func(){cout << "Derive::Func1()" << endl;}
};void Test(Base* p)
{p->Func();
}int main()
{Base b;Derive d;Test(&b);return 0;
}
監(jiān)視窗口
通過(guò)監(jiān)視窗口可以發(fā)現(xiàn)
派生類(lèi)對(duì)象d中也有一個(gè)虛表指針
通過(guò)地址發(fā)現(xiàn)基類(lèi)和派生類(lèi)的虛表是不一樣的
虛函數(shù)表本質(zhì)是存虛函數(shù)指針的指針數(shù)組
一般情況這個(gè)數(shù)組最后面放了一個(gè)nullptr
結(jié)論:
觀察下圖紅色箭頭
當(dāng)傳過(guò)來(lái)的是父類(lèi)對(duì)象的地址
p->Func在父類(lèi)的虛表中找對(duì)應(yīng)的虛函數(shù)Func地址
當(dāng)傳過(guò)來(lái)的是子類(lèi)對(duì)象的地址
p->Func在子類(lèi)的虛表中找對(duì)應(yīng)的虛函數(shù)Func地址
這樣就實(shí)現(xiàn)不同對(duì)象的同一行為
展現(xiàn)的不同狀態(tài)
當(dāng)父類(lèi)有虛函數(shù)而子類(lèi)沒(méi)有虛函數(shù)
也沒(méi)有重名函數(shù)
子類(lèi)是不能繼承父類(lèi)的虛表指針
子類(lèi)會(huì)生成一個(gè)虛表指針
存父類(lèi)的虛函數(shù)地址
當(dāng)子類(lèi)有虛函數(shù)而父類(lèi)沒(méi)有
父類(lèi)也就不會(huì)有虛表
因?yàn)樽宇?lèi)有的東西父類(lèi)不一定有
派生類(lèi)的虛表生成:
a.
先將基類(lèi)中的虛表內(nèi)容拷貝一份到派生類(lèi)虛表中
b.
如果派生類(lèi)重寫(xiě)了基類(lèi)中某個(gè)虛函數(shù)
用派生類(lèi)自己的虛函數(shù)覆蓋虛表中基類(lèi)的虛函數(shù)
c.
派生類(lèi)自己新增加的虛函數(shù)按其在派生類(lèi)中的
聲明次序增加到派生類(lèi)虛表的最后
2.3 在多態(tài)下建議把基類(lèi)的析構(gòu)函數(shù)定義成虛函數(shù)
如果基類(lèi)析構(gòu)函數(shù)不是虛函數(shù)
就調(diào)不到派生類(lèi)的析構(gòu)函數(shù)
(指針類(lèi)型是父類(lèi),所以調(diào)用父類(lèi)的析構(gòu)函數(shù))
從而造成內(nèi)存泄漏
class Person {
public:virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:virtual ~Student() { cout << "~Student()" << endl; }
};int main()
{Person* ps = new Student;delete ps;return 0;
}
形成多態(tài)的條件之一便是
只能通過(guò)父類(lèi)去調(diào)用
所以子類(lèi)對(duì)象只能強(qiáng)轉(zhuǎn)成父類(lèi)類(lèi)型
如果父類(lèi)的析構(gòu)函數(shù)不是虛函數(shù)
那子類(lèi)便調(diào)不到自己的析構(gòu)函數(shù)
因?yàn)樽宇?lèi)對(duì)象的類(lèi)型是父類(lèi)
所以只能調(diào)用父類(lèi)的析構(gòu)函數(shù)
子類(lèi)成員無(wú)法釋放從而造成內(nèi)存泄漏
父類(lèi)析構(gòu)函數(shù)定義成虛函數(shù)便能解決問(wèn)題
????????
本篇博客完,感謝閱讀🌹
如有錯(cuò)誤之處可評(píng)論指出
博主會(huì)耐心聽(tīng)取每條意見(jiàn)
????????