C++中的虛擬函式與虛擬函式表 (轉)
學習 C++ 的同志不知道有沒有和我一樣遇到過這樣的困惑:C++中的虛到底怎麼實現的?在各種繼承關係中,虛擬函式表的結構到底是什麼樣的?曾經我是很想當然,可是後來在使用ATL的過程中,我發現並不是我想的那樣。大家知道,利用C++語言本身的特性進行COM當然是很方便的事,但是你就得隨時隨地都知道那虛擬函式表裡頭到底是些什麼東西。講C++語法的書沒有義務告訴你C++產生的虛擬函式表是什麼樣的,這就是頭痛的所在。
自已做試驗是件很快樂的事,我很願意這麼做。
首先寫個函式,作為我們實驗的基礎。傳入虛擬函式表指標,顯示虛數表的內容。
void DispVFT(D* pVFT)
{
printf("VFT Pointer:%pn" , pVFT);
printf("Beginn");
DWORD* p = (DWORD* )*pVFT;//得到VFT的首址
while(*p) 個地方我是看錶項是不是為空來判斷是否到了表尾,
多數情況都是對的,不過不能為準
{
printf("VF:%p , %pn", p , *p);
p++;
}
printf("Endnn");
}
首先我們看單個類時的虛擬函式表的情況:
class C1
{
public:
C1()
{
("In C1n");
((DWORD*)this);
};
virtual F1()
{}
};
void main()
{
C1 c1;
於C1中沒有成員資料,所有我們可以用這種方式判斷
中的虛擬函式表指標的個數
printf("vtr count :%dn" , sizeof(C1) / 4);
示結構
DispVFT((DWORD* )&c1);
}
輸出:
vftptr count :1
VFT Pointer:0012FF7C
Begin
VF:00420048 , 00401078(C1::F1)
End
很單純,不用多講,這是我們意料之中的結果。
下面我們進行簡單繼承的實驗
class C2 : public C1
{
public:
C2(){
printf("In C1n");
DispVFT((DWORD*)this);
}
virtual F2()
{}
};
void main()
{
C2 c2;
C1* pC1 = &c2;
printf("vftptr count :%dn" , sizeof(C2) / 4);
printf("C1n");
DispVFT((DWORD*)pC1);
printf("C2n");
DispVFT((DWORD*)&c2);
}
輸出:
In C1
VFT Pointer:0012FF7C
Begin
VF:00420048 , 00401087(C1::F1)
End
In C2
VFT Pointer:0012FF7C
Begin
VF:004200F4 , 00401087(C1::F1) 出的第一項是表的首址與對應的表項內容,看看地址,與 In C1的不同,說明是不同的兩個表
VF:004200F8 , 0040108C(C2::F2)
End
vftptr count :1
C1
VFT Pointer:0012FF7C
Begin
VF:004200F4 , 00401087(C1::F1)
VF:004200F8 , 0040108C(C2::F2)
End
C2
VFT Pointer:0012FF7C
Begin
VF:004200F4 , 00401087(C1::F1)
VF:004200F8 , 0040108C(C2::F2)
End
大家可以看到最後虛擬函式表指標仍然是同一個,表中按順序放入了C1(基類)與C2(派生類)的虛擬函式指標。
下面是多重繼承
class C1
{
public:
C1()
{
("In C1n");
((DWORD*)this);
}
virtual F1(){}
};
class C2
{
public:
C2(){
("In C1n");
((DWORD*)this);
}
virtual F2(){}
};
class C3 : public C1 , public C2
{
public:
C3(){
("In C1n");
((DWORD*)this);
}
virtual F3(){}
};
void main()
{
C3 c3;
C2* pC2 = &c3;
C1* pC1 = &c3;
printf("vftptr count :%dn" , sizeof(C3) / 4);
printf("C1n");
DispVFT((DWORD*)pC1);
printf("C2n");
DispVFT((DWORD*)pC2);
printf("C3n");
DispVFT((DWORD*)&c3);
}
輸出:
vftptr count :2
C1
VFT Pointer:0012FF78
Begin
VF:00420104 , 00401046(C1::F1)
VF:00420108 , 0040101E(C3::F3)
End
C2
VFT Pointer:0012FF7C
Begin
VF:00420100 , 00401028(C2::F2)
VF:00420104 , 00401046(C1::F1)
VF:00420108 , 0040101E(C3::F3)
End
C3
VFT Pointer:0012FF78
Begin
VF:00420104 , 00401046(C1::F1)
VF:00420108 , 0040101E(C3::F3)
End
虛擬函式表指標變成兩個了,也就是說現在是用兩個虛擬函式表指標維護一個表,總結一下就是,虛擬函式表指標的個數等於基類的個數。至於虛擬函式表的個數,應該是三個。你可以把我在建構函式中加的程式碼去掉註釋符,看看輸出。你會發現每次輸出的表的首地址都是不一樣,那表當然也不是同一個表。
下面說說多層繼承的情況:
class C1
{
public:
C1()
{
printf("In C1n");
DispVFT((DWORD*)this);
}
virtual F1(){}
};
class C2 : public C1
{
public:
C2(){
printf("In C2n");
DispVFT((DWORD*)this);
}
virtual F2(){}
};
class C3 : public C2
{
public:
C3(){
printf("In C3n");
DispVFT((DWORD*)this);
}
virtual F3(){}
};
void main()
{
C3 c3;
C2* pC2 = &c3;
C1* pC1 = &c3;
printf("vftptr count :%dn" , sizeof(C3) / 4);
printf("C1n");
DispVFT((DWORD*)pC1);
printf("C2n");
DispVFT((DWORD*)pC2);
printf("C3n");
DispVFT((DWORD*)&c3);
}
輸出:
In C1
VFT Pointer:0012FF7C
Begin
VF:00421090 , 00401046(C1::F1)
End
In C2
VFT Pointer:0012FF7C
Begin
VF:0042010C , 00401046(C1::F1) 裡是類C2的vftable,第一個輸出是它首址與表項內容
VF:00420110 , 00401028(C2::F2)
End
In C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046(C1::F1)
VF:004210A0 , 00401028(C2::F2)
VF:004210A4 , 00401078(C3::F3)
End
vftptr count :1
C1
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C2
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
得到的結果:我們看到了虛擬函式表指標是一個,可是你仔細看看每個建構函式的輸出!輸出的第一項是表的首址與對應的表項。大家可以看到,首址都是不一樣的這說明是三個不同的表,那麼這個類就有三個虛擬函式表。你可能會想,這三個表在什麼時候用呢,事實上,在C3的例項被構造出來後,只有最後一個表,也就是C3的表在用,其它的表跟本就是沒有用的,C++在沒有透過你同意的情況下,在浪費你的空間(多重繼承也存在同樣的問題)。想了個辦法把其它的不用的虛擬函式表去掉:__declspec(novtable)
class __declspec(novtable)C1
{
public:
C1()
{
("In C1n");
((DWORD*)this); 裡得去掉,既然沒有那個表,怎麼輸出
}
virtual F1(){}
};
class __declspec(novtable)C2 : public C1
{
public:
C2(){
("In C2n");
((DWORD*)this);//這裡得去掉,既然沒有那個表,怎麼輸出
}
virtual F2(){}
};
class C3 : public C2
{
public:
C3(){
printf("In C3n");
DispVFT((DWORD*)this);
}
virtual F3(){}
};
void main()
{
C3 c3;
C2* pC2 = &c3;
C1* pC1 = &c3;
printf("vftptr count :%dn" , sizeof(C3) / 4);
printf("C1n");
DispVFT((DWORD*)pC1);
printf("C2n");
DispVFT((DWORD*)pC2);
printf("C3n");
DispVFT((DWORD*)&c3);
}
輸出:
In C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
vftptr count :1
C1
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C2
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
可以看到一切正常,只是在不知不覺中,你的瘦身成功,不過如果你決定要去掉類的虛擬函式表,你最好可以確定這個類應該是個被繼承的基類,而不是最後派生使用的類。否則可能會出錯,比如:
void func1(C1* p)
{
p->F1();
}
void main()
{
C2 c2;
func1(&c2);
}
這種情況就會出錯,沒有虛擬函式表,虛擬函式的怎麼能實現呢?
如果說得有什麼不對,望同志們指正賜教。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992745/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 虛擬函式,虛擬函式表函式
- c++虛擬函式表C++函式
- 【C++筆記】虛擬函式(從虛擬函式表來解析)C++筆記函式
- 虛擬函式 純虛擬函式函式
- 深入C++成員函式及虛擬函式表C++函式
- 【C++筆記】虛擬函式(從虛擬函式概念來解析)C++筆記函式
- 介面、虛擬函式、純虛擬函式、抽象類函式抽象
- C++ 介面(純虛擬函式)C++函式
- C++ 虛擬函式表解析C++函式
- C++物件導向總結——虛指標與虛擬函式表C++物件指標函式
- 虛擬函式與多型函式多型
- [Lang] 虛擬函式函式
- C++多型之虛擬函式C++多型函式
- 虛擬函式表-C++多型的實現原理函式C++多型
- 虛擬函式的呼叫原理函式
- C++ 派生類函式過載與虛擬函式繼承詳解C++函式繼承
- C++虛擬函式學習總結C++函式
- 內聯(inline)函式與虛擬函式(virtual)的討論inline函式
- 詳解C++中的多型和虛擬函式C++多型函式
- 虛擬函式的實現原理函式
- C++之類解構函式為什麼是虛擬函式C++函式
- C++建構函式和解構函式呼叫虛擬函式時使用靜態聯編C++函式
- C++純虛擬函式簡介及區別C++函式
- c++虛擬函式實現計算表示式子C++函式
- C++單繼承、多繼承情況下的虛擬函式表分析C++繼承函式
- 抽象基類和純虛擬函式抽象函式
- C++(虛擬函式實現多型基本原理)C++函式多型
- 虛擬函式的記憶體佈局(上)函式記憶體
- 關於虛擬函式的一些理解函式
- 基類指標、虛純虛擬函式、多型性、虛析構指標函式多型
- Java常見知識點彙總(④)——虛擬函式、抽象函式、抽象類、介面Java函式抽象
- setV:一個管理 Python 虛擬環境的 Bash 函式Python函式
- 基類指標,子類指標,虛擬函式,override與final指標函式IDE
- 避免對派生的非虛擬函式進行重定義函式
- 深度解讀《深度探索C++物件模型》之C++虛擬函式實現分析(二)C++物件模型函式
- 深度解讀《深度探索C++物件模型》之C++虛擬函式實現分析(一)C++物件模型函式
- 深度解讀《深度探索C++物件模型》之C++虛擬函式實現分析(三)C++物件模型函式
- <<從0到1學C++>> 第7篇 多型性和虛擬函式C++多型函式
- C++中函式指標與函式物件C++函式指標物件