怎么搞一個(gè)網(wǎng)站seo是什么職業(yè)做什么的
前言
在前面內(nèi)容中,我們對多態(tài)進(jìn)行了基本的了解,對其中的虛函數(shù)進(jìn)行著重的介紹,本節(jié)內(nèi)容我們將進(jìn)一步對多態(tài)的底層進(jìn)行觀察了解看看它是如何實(shí)現(xiàn)的。
多態(tài)如何實(shí)現(xiàn)
從底層的角度Func函數(shù)中ptr->BuyTicket(),是如何作為ptr指向Person對象調(diào)Person::BuyTicket,
ptr指向Student對象調(diào)用Student::BuyTicket的呢?
通過下圖我們可以看到,滿足多態(tài)條件后,底層不再是編譯時(shí)通過調(diào)用對象確定函數(shù)的地址,而是運(yùn)行時(shí)到指向的對象的虛表中確定對應(yīng)的虛函數(shù)的地址,這樣就實(shí)現(xiàn)了指針或引用指向基類就調(diào)用基類的虛函數(shù),指向派生類就調(diào)用派生類對應(yīng)的虛函數(shù)。
第一張圖,ptr指向的Person對象,調(diào)用的是Person的虛函數(shù);第二張圖,ptr指向的Student對
象,調(diào)用的是Student的虛函數(shù)。

class Person {
public:virtual void BuyTicket() { cout << "買票-全價(jià)" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "買票-打折" << endl; }
};
class Soldier : public Person {
public:virtual void BuyTicket() { cout << "買票-優(yōu)先" << endl; }
};
void Func(Person* ptr)
{// 這?可以看到雖然都是Person指針Ptr在調(diào)?BuyTicket// 但是跟ptr沒關(guān)系,?是由ptr指向的對象決定的。ptr->BuyTicket();
}
int main()
{Person ps;Student st;Soldier sr;Func(&ps);Func(&st);Func(&sr);return 0;
}
其次多態(tài)不僅僅發(fā)生在派生類對象之間,多個(gè)派生類繼承基類,重寫虛函數(shù)后
多態(tài)也會(huì)發(fā)生在多個(gè)派生類之間。?
動(dòng)態(tài)綁定與靜態(tài)綁定
對不滿足多態(tài)條件(指針或者引用+調(diào)用虛函數(shù))的函數(shù)調(diào)用是在編譯時(shí)綁定,也就是編譯時(shí)確定調(diào)用函數(shù)的地址,叫做靜態(tài)綁定。
滿足多態(tài)條件的函數(shù)調(diào)用是在運(yùn)行時(shí)綁定,也就是在運(yùn)行時(shí)到指向?qū)ο蟮奶摵瘮?shù)表中找到調(diào)用函數(shù)
的地址,也就做動(dòng)態(tài)綁定。
// ptr是指針+BuyTicket是虛函數(shù)滿?多態(tài)條件。
// 這?就是動(dòng)態(tài)綁定,編譯在運(yùn)?時(shí)到ptr指向?qū)ο蟮奶摵瘮?shù)表中確定調(diào)?函數(shù)地址
ptr->BuyTicket();
00EF2001 mov eax,dword ptr [ptr]
00EF2004 mov edx,dword ptr [eax]
00EF2006 mov esi,esp
00EF2008 mov ecx,dword ptr [ptr]
00EF200B mov eax,dword ptr [edx]
00EF200D call eax
// BuyTicket不是虛函數(shù),不滿?多態(tài)條件。
// 這?就是靜態(tài)綁定,編譯器直接確定調(diào)?函數(shù)地址
ptr->BuyTicket();
00EA2C91 mov ecx,dword ptr [ptr]
00EA2C94 call Student::Student (0EA153Ch)
虛函數(shù)表
虛函數(shù)表(Virtual Function Table,也常簡稱為vtable)是C++語言中實(shí)現(xiàn)多態(tài)的一種機(jī)制。當(dāng)一個(gè)類聲明或繼承了一個(gè)或多個(gè)虛函數(shù)時(shí),編譯器會(huì)為這個(gè)類創(chuàng)建一個(gè)虛函數(shù)表,這個(gè)表中包含了類中所有虛函數(shù)的地址。
以下是對虛函數(shù)表的基本解釋:
- 虛函數(shù):在基類中被聲明為虛函數(shù)的成員函數(shù)可以在派生類中被重寫(override),以實(shí)現(xiàn)多態(tài)。
- 虛函數(shù)表:每個(gè)具有虛函數(shù)的類都有自己的虛函數(shù)表。當(dāng)對象被創(chuàng)建時(shí),它的內(nèi)存布局中會(huì)包含一個(gè)指向其類虛函數(shù)表的指針,通常被稱為vptr(virtual table pointer)。
虛函數(shù)表的工作流程如下:
- 當(dāng)一個(gè)類的對象被創(chuàng)建時(shí),對象的內(nèi)存布局中包含一個(gè)vptr,它指向該類的虛函數(shù)表。
- 當(dāng)通過基類的指針或引用調(diào)用一個(gè)虛函數(shù)時(shí),程序會(huì)根據(jù)對象的vptr找到對應(yīng)的虛函數(shù)表,并從表中獲取正確版本的函數(shù)地址來調(diào)用。
- 如果派生類重寫了基類的虛函數(shù),虛函數(shù)表中相應(yīng)函數(shù)的條目會(huì)被更新為指向派生類中重寫后的函數(shù)。
派生類的虛函數(shù)表中包含,基類的虛函數(shù)地址,派生類重寫的虛函數(shù)地址,派生類自己的虛函數(shù)地址三個(gè)部分。
虛函數(shù)表本質(zhì)是一個(gè)存虛函數(shù)指針的指針數(shù)組,一般情況這個(gè)數(shù)組最后面放了一個(gè)0x00000000標(biāo) 記。(這個(gè)C++并沒有進(jìn)?規(guī)定,各個(gè)編譯器自行定義的,vs系列編譯器會(huì)再后面放個(gè)0x00000000 標(biāo)記,g++系列編譯不會(huì)放)
虛函數(shù)存在哪的?虛函數(shù)和普通函數(shù)一樣的,編譯好后是一段指令,都是存在代碼段的,只是虛函 數(shù)的地址又存到了虛表中。
虛函數(shù)表存在哪的?這個(gè)問題嚴(yán)格說并沒有標(biāo)準(zhǔn)答案C++標(biāo)準(zhǔn)并沒有規(guī)定,
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
};
class Derive : public Base
{
public:// 重寫基類的func1virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func1" << endl; }void func4() { cout << "Derive::func4" << endl; }
protected:int b = 2;
};
int main()
{Base b;Derive d;return 0;
}
注意:這里Derive中沒有看到func3函數(shù),這個(gè)vs監(jiān)視窗口看不到,可以通過內(nèi)存窗口查看?
也可以打印出來
int main()
{int i = 0;static int j = 1;int* p1 = new int;const char* p2 = "xxxxxxxx";printf("棧:%p\n", &i);printf("靜態(tài)區(qū):%p\n", &j);printf("堆:%p\n", p1);printf("常量區(qū):%p\n", p2);Base b;Derive d;Base* p3 = &b;Derive* p4 = &d;printf("Person虛表地址:%p\n", *(int*)p3);printf("Student虛表地址:%p\n", *(int*)p4);printf("虛函數(shù)地址:%p\n", &Base::func1);printf("普通函數(shù)地址:%p\n", &Base::func5);return 0;
}
結(jié)束語
本節(jié)內(nèi)容就到此結(jié)束啦,本次內(nèi)容了解即可,只是為了更好的理解多態(tài)的原理。?