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