多型呼叫的原理
阿新 • • 發佈:2018-12-10
問題:為什麼派生類對基類的虛擬函式重寫,通過基類物件的指標或引用>呼叫該虛擬函式,就可以實現多型
因為有序函式的類的物件模型的前四個位元組儲存的是一個地址,這個地址指向的是一個虛擬函式表,表中的內容是虛擬函式的地址。
#include <iostream> #include <string> using namespace std; typedef void(*PVFT)(); //定義一個函式指標 class Base { public: virtual void TestFun1() { cout << "Base::TestFunc1()"<<endl; } virtual void TestFun2() { cout << "Base::TestFunc2()" << endl; } virtual void TestFunc3() { cout << "Base::TestFunc3()" << endl; } int _b; }; void printVTF(Base& b,const string& desc) { //(&b) //取類b的地址(指標) //(int*)(&b) //因為需要前四個位元組的內容,所以對其強轉為(int*) //*(int*)(&b) //將前四個位元組的內容取到 //(PVFT*)*(int*)(&b) //將前四個位元組的內容取到作為地址 //賦值給函式指標 cout <<desc<< endl; PVFT* PV = (PVFT*)*(int*)(&b); while (*PV) { (*PV)(); ++PV; } } int main() { cout << sizeof(Base) << endl; Base b; b._b = 10; printVTF(b,"Base"); system("pause"); return 0; }
普通函式:直接呼叫
虛擬函式的呼叫(滿足多型條件)
- 從物件前4個位元組中取虛表的地址
- 傳遞this指標
- 從虛表中獲取虛擬函式的地址(在虛表地址+虛擬函式在虛表中的偏移量)
- 呼叫虛擬函式
單繼承
- 基類有虛擬函式,繼承基類,派生類將基類的虛表拷貝到自己的虛表中
- 派生類某個虛擬函式如果重寫基類的虛擬函式,替代相同偏移量位置的虛擬函式為子類的虛擬函式
- 如果派生類新增加自己特有的虛擬函式,將其放在基類繼承下來虛擬函式的後面。
#include <iostream> #include <string> using namespace std; typedef void(*PVFT)(); class Base { public: virtual void TestFun1() { cout << "Base::TestFunc1()" << endl; } virtual void TestFun2() { cout << "Base::TestFunc2()" << endl; } virtual void TestFunc3() { cout << "Base::TestFunc3()" << endl; } int _b; }; void printVTF(Base& b, const string& desc) { //(&b) //取類b的地址(指標) //(int*)(&b) //因為需要前四個位元組的內容,所以對其強轉為(int*) //*(int*)(&b) //將前四個位元組的內容取到 //(PVFT*)*(int*)(&b) //將前四個位元組的內容取到作為地址 //賦值給函式指標 cout << desc << endl; PVFT* PV = (PVFT*)*(int*)(&b); while (*PV) { (*PV)(); ++PV; } } class Derived : public Base { public: virtual void TestFun2() { cout << "Derived::TestFunc2()" << endl; } virtual void TestFunc3() { cout << "Derived::TestFunc3()" << endl; } int _d; }; int main() { cout << sizeof(Base) << endl; cout << sizeof(Derived) << endl; Derived d; d._b = 1; d._d = 2; printVTF(d, "Deived VIF:"); system("pause"); return 0; }
帶有虛擬函式的多繼承物件的模型以及虛表
- 多繼承,將派生類自己的新增加的虛擬函式新增到第一張虛表最後
#include <iostream> #include <string> using namespace std; class B1 { public: virtual void TestFunc1() { cout << "B1::TestFunc1" << endl; } virtual void TestFunc2() { cout << "B1::TestFunc2" << endl; } int _b1; }; class B2 { public: virtual void TestFunc3() { cout << "B2::TestFunc3" << endl; } virtual void TestFunc4() { cout << "B2::TestFunc4" << endl; } int _b2; }; class D:public B1, public B2 { public: virtual void TestFunc1() { cout << "D::TestFunc1" << endl; } virtual void TestFunc4() { cout << "D::TestFunc4" << endl; } virtual void TestFunc5() { cout << "D::TestFunc5" << endl; } int _d; }; typedef void(*PVTF)(); void PrintVTF(B1& b,const string& str) //不能傳值(不是外面的實體),必須指標或引用, { PVTF* PV = (PVTF*)(*(int*)&b); cout << str << endl; while (*PV) { (*PV)(); ++PV; } } void PrintVTF(B2& b, const string& str) //不能傳值(不是外面的實體),必須指標或引用, { PVTF* PV = (PVTF*)(*(int*)&b); cout << str << endl; while (*PV) { (*PV)(); ++PV; } } int main() { cout << sizeof(D) << endl; D d; d._b1 = 1; d._b2 = 2; d._d = 3; B1& b1 = d; PrintVTF(b1,"D VTF of B1"); B2& b2 = d; PrintVTF(b2, "D VTF of B2"); system("pause"); return 0; }
上面程式碼的輸出結果
帶有虛擬函式的菱形繼承
#include <iostream>
#include <string>
using namespace std;
class B
{
public:
virtual void TestFunc1()
{
cout << "B::TestFunc1()" << endl;
}
virtual void TestFunc2()
{
cout << "B::TestFunc2()" << endl;
}
int _b;
};
class C1 : public B
{
public:
virtual void TestFunc1()
{
cout << "C1::TestFunc1()" << endl;
}
virtual void TestFunc3()
{
cout << "C1::TestFunc3()" << endl;
}
int _c1;
};
class C2 : public B
{
public:
virtual void TestFunc2()
{
cout << "C2::TestFunc2()" << endl;
}
virtual void TestFunc4()
{
cout << "C2::TestFunc4()" << endl;
}
int _c2;
};
class D : public C1, public C2
{
public:
virtual void TestFunc3()
{
cout << "D::TestFunc3()" << endl;
}
virtual void TestFunc4()
{
cout << "D::TestFunc4()" << endl;
}
virtual void TestFunc5()
{
cout << "D::TestFunc5()" << endl;
}
int _d;
};
typedef void(*PVTF)();
void PrintVTF(C1& b, const string& str) //不能傳值(不是外面的實體),必須指標或引用,
{
PVTF* PV = (PVTF*)(*(int*)&b);
cout << str << endl;
while (*PV)
{
(*PV)();
++PV;
}
}
void PrintVTF(C2& b, const string& str) //不能傳值(不是外面的實體),必須指標或引用,
{
PVTF* PV = (PVTF*)(*(int*)&b);
cout << str << endl;
while (*PV)
{
(*PV)();
++PV;
}
}
int main()
{
cout << sizeof(D) << endl;
D d;
d.C1::_b = 0;
d._c1 = 1;
d.C2::_b = 2;
d._c2 = 3;
d._d = 4;
C1& c1 = d;
PrintVTF(c1,"D VTF of c1");
C2& c2 = d;
PrintVTF(c2, "D VTF of c2");
system("pause");
return 0;
}