1.作用:
用于有虛函數對象的指針,其在運作期間決定實際應該執行的函數的位址
2.記憶體布局:
記憶體的開頭位置(64位),即記憶體開頭8位元組内容為vtable的的位址值
而vtable順序存放函數位址值(64位順序數組)
3.代碼調用:
#include <stdio.h>
#include <iostream>
class P {
public:
virtual void test() {std::cout << "test " << std::endl;}
virtual void testa() {std::cout << "testa " << std::endl;}
private:
int a;
};
class S : public P {
public:
void test() {std::cout << "test s" << std::endl;}
void testc() {std::cout << "test s c" << std::endl;}
private:
int c;
};
int main() {
P *p = new S();
void *ptr = (void*)(*((long*)p));
printf("%x\n", ptr);
void (*fun)(void) = (void(*)(void))(*((long*)ptr));
printf("%x\n", fun);
(fun)();
delete p;
return 0;
}
void *ptr = (void*)(*((long*)p)); 擷取到vtable的位址
void (*fun)(void) = (void(*)(void))(*((long*)ptr));擷取函數指針
注:
1.隻有擁有virtual的類的對象才會有vtable,對于非virtual的函數,在編譯器就能直接指定調用,而無需額外操作來得到
case:
析構函數為什麼一般需要virtual;
假設P是S的父類,那麼預設情況下S的析構函數生成的代碼會自動包含調用父類析構函數的代碼,
這也就能說明當使用S s;等本地變量的時候,其是能夠處理完自己的析構函數再自動調用父類的析構函數(構造函數則相反),即代碼已經在子類的析構函數中,
但是當用P *p = new S();的時候,如果p的析構函數不是virtual的,那麼會隻有析構~P函數,這是因為,~P()并不是虛函數,那麼不會在vtable中,其決定析構函數的調用,
在編譯期間便根據P的類型,直接隻調用~P的
那麼當~P為虛函數的時候,~P是作為vtable的函數的,當其指向S()的時候,其~P()的函數實際是指向~S()的,是以便能完成析構函數的正确調用。