有那么多講C++多態(tài)的文章,但是卻沒有一個能真正看明白的,神秘的多態(tài)機制,究竟是如何實現的镣陕,看黃老師來如何教你?
我們先看兩個類:
class A
{
public:
A() { a = 111; }
virtual void? fun1()
{
cout << " A::fun1" << endl;
}
virtual void fun2()
{
cout << " A::fun2" << endl;
}
int a;
};
class? B:public A
{
public:
B() { b = 222; }
virtual void? fun2()
{
cout << " B::fun2" << endl;
}
virtual void fun3()
{
cout << " B::fun3" << endl;
}
int b;
};
簡單介紹下規(guī)則:
1. 首先對于含有virtual關鍵字的類(A? 或? B)姻政, 該類的所有對象的內存的前四個字節(jié)(32位系統(tǒng)下)呆抑,存放一個內存地址,根據這個地址可以訪問到該類的虛函數表內存(表中的每一項都是一個函數地址 汁展,所以A中有兩項? fun1? 鹊碍,fun2 )。
2. 派生類(自己有virtual 或者 基類中有 virtual 關鍵字)食绿,? 根據1規(guī)則侈咕,同樣有一張?zhí)摵瘮当韮却?( 表中的每一項都是一個函數地址,按道理來說應該是四項? ?A類中的 fun1? 器紧,fun2 耀销, B類中的fun2, fun3 铲汪,這個時候需要注意熊尉,派生類的fun2 會覆蓋基類的fun2, 所以只有三項)?
用圖解釋如下:
作為一個最喜歡扒內存的黃老師而言桥状,利用代碼測試其內存結構帽揪,便能一清二楚!8ㄕ濉转晰!
#include <iostream>?
using namespace std;
typedef? void(*PFUN)();
class? A
{
public:
A() { a = 111; }
virtual void? fun1()
{
cout << " A::fun1" << endl;
}
virtual void fun2()
{
cout << " A::fun2" << endl;
}
int a;
};
class? B:public A
{
public:
B() { b = 222; }
virtual void? fun2()
{
cout << " B::fun2" << endl;
}
virtual void fun3()
{
cout << " B::fun3" << endl;
}
int b;
};
int main()
{
//根據多態(tài)我們知道,肯定調用的是B的fun2
//A? *p = new B;
//p->fun2();
cout << "---------------------------A的虛函數表--------------------------" << endl;
{
cout << "A類的大小 " << sizeof(A) << endl;
//測試A的虛函數表
A? ? a;
int *pA = (int *)&a;//a內存空間的首地址
cout << "4字節(jié)vtable指針"<<endl;
int * vptr = (int *)(*pA); // a內存空間的 前4個字節(jié) 存放的內容是? 虛函數表的地址
PFUN? afun1 = (PFUN)vptr[0];
afun1();
PFUN? afun2 = (PFUN)vptr[1];
afun2();
cout <<"4字節(jié)a成員"<< *(pA + 1)<<endl;? //成員變量a的內容
}
cout << "---------------------------B的虛函數表--------------------------" << endl;?
{
cout << "B類的大小 " << sizeof(B) << endl;
//測試B的虛函數表
B? ? b;
int *pB = (int *)&b;//b內存空間的首地址
cout << "4字節(jié)vtable指針" << endl;? //成員變量a的內容
int * vptr = (int *)(*pB); // b內存空間的 前4個字節(jié) 存放的內容是? 虛函數表的地址
PFUN? bfun1 = (PFUN)vptr[0];
bfun1();
PFUN? bfun2 = (PFUN)vptr[1];
bfun2();
PFUN? bfun3 = (PFUN)vptr[2];
bfun3();
cout << "4字節(jié)a成員" << *(pB + 1) << endl;? //成員變量a的內容
cout << "4字節(jié)b成員" << *(pB + 2) << endl;? //成員變量b的內容
}
}
測試結果與示意圖一樣: