昨天,收到SenseTime公司面試官的電話面試(一天面了三家公司,收獲挺多的),通話時間將近1個半小時,面試過程中暴露出很多知識上的漏洞,本篇文章針對面試過程中繼承以及虛函數方面的知識做一總結,查缺補漏,希望對大家有幫助。
單繼承下的虛函數表
//單繼承下虛函數表:是如何組織的 class A{ public: virtual void func(){ cout << "A::func" << endl; } virtual void funcA(){ cout << "A::funcA" << endl; } }; class B:public A{ public: virtual void func(){ cout << "B::func" << endl; } virtual void funcB(){ cout << "B::funcB" << endl; } }; class C:public A{ public: virtual void func(){ cout << "C::func" << endl; } virtual void funcC(){ cout << "C::funcC" << endl; } }; typedef void (*FUNC)(); int main() { A a; B b; C c; cout << "A::虛表:" << endl; ((FUNC)(*(int *)(*(int*)(&a))))(); ((FUNC)(*((int*)(*(int*)(&a)) + 1)))(); cout << "-------------------------------------" << endl; cout << "B::虛表:" << endl; ((FUNC)(*(int *)(*(int*)(&b))))(); ((FUNC)(*((int*)(*(int*)(&b)) + 1)))(); ((FUNC)(*((int*)(*(int*)(&b)) + 2)))(); cout << "-------------------------------------" << endl; cout << "C::虛表:" << endl; ((FUNC)(*(int *)(*(int*)(&c))))(); ((FUNC)(*((int*)(*(int*)(&c)) + 1)))(); ((FUNC)(*((int*)(*(int*)(&c)) + 2)))(); system("pause"); return 0; }
問題1:三個類中都有虛函數,所以三個類均有各自獨立的虛函數表
問題2:三個虛函數表的組織情況,如上圖所示
問題3:B、C各自擁有自己的虛函數表,互不影響
問題4:類的對象只存儲指向虛函數表的指針vfptr(一般存儲在對象內存布局的最前面),虛函數表只有一份,為所有對象所共享,vtable在Linux/Unix中存放在可執行文件的只讀數據段中(rodata),而微軟的編譯器將虛函數表存放在常量段點擊打開鏈接
typedef void(*FUNC)(); class A{ public: virtual void func(){ cout << "A::func" << endl; } virtual void funcA(){ cout << "A::funcA" << endl; } private: int a; }; class B{ public: virtual void func(){ cout << "B::func" << endl; } virtual void funcB(){ cout << "B::funcB" << endl; } private: int b; }; class C :public A, public B{ public: virtual void func(){ cout << "C::func" << endl; } virtual void funcC(){ cout << "C::funcC" << endl; } private: int c; };typedef void(*FUNC)(); class A{ public: virtual void func(){ cout << "A::func" << endl; } virtual void funcA(){ cout << "A::funcA" << endl; } private: int a; }; class B{ public: virtual void func(){ cout << "B::func" << endl; } virtual void funcB(){ cout << "B::funcB" << endl; } private: int b; }; class C :public A, public B{ public: virtual void func(){ cout << "C::func" << endl; } virtual void funcC(){ cout << "C::funcC" << endl; } private: int c; };
多繼承條件下的虛函數表
//多繼承條件下的虛函數表 void test() { C c; cout << "多繼承條件下的虛函數表:" << endl; cout << "------------------------" << endl; ((FUNC)(*((int*)(*(int *)(&c)))))(); ((FUNC)(*((int*)(*(int*)(&c)) + 1)))(); ((FUNC)(*((int*)(*(int*)(&c)) + 2)))(); cout << "------------------------" << endl; ((FUNC)(*(int*)(*((int*)(&c) + 2))))(); ((FUNC)(*((int*)(*((int*)(&c) + 2)) + 1)))(); }
對象C的內存空間分布如下圖所示:
多繼承條件下,基類指針指向派生類後,基類指針所能訪問的函數
//多繼承條件下,基類指針指向派生類後,基類指針所能訪問的函數 void test1() { C c; A *pa = &c; B *pb = &c; C *pc = &c; cout << "基類指針pa所能調用的函數:" << endl; pa->func(); pa->funcA(); //pa->funcB();error:提示類A沒有成員funcB、funcC -->受到類型的限制 //pa->funcC();error cout << "基類指針pb所能調用的函數:" << endl; pb->func(); pb->funcB(); //pb->funcA();error //pb->funcC();error cout << "派生類指針pc所能調用的函數:" << endl; pc->func(); pc->funcA(); pc->funcB(); pc->funcC(); }
問題1:pa和pc的值相同,都是對象c的首地址,pb和pa至今相差四個字節(int a造成的-->觀察上圖的內存空間分配)
問題2:基類指針指向派生類的對象,通過該基類指針所能訪問的函數受類型的限制(運行時調用哪個函數受多態的影響)
3:由於多態,會訪問C類的func
4:通過加作用域:pa->A::func()
多繼承條件下,基類指針指向派生類對象後,基類指針之間強制類型轉化之後,所能訪問的函數
void test2() { C c; A *pa = &c; B *pb = &c; C *pc = &c; pa = reinterpret_cast(pb); pa->func(); pa->funcA(); //pb = reinterpret_cast(pa); //pb->func(); //pb->funcB(); //pa = reinterpret_cast(pc); //pa->func(); //pa->funcA(); }
結果很奇怪(查看匯編分析)
最後一個簡單地問題如下
相信你經過以上問題的分析,一定會回答出來(答案已經在圖裡面了^_^)