C 中虛擬函式 虛表指標的問題

2025-07-16 14:30:07 字數 4846 閱讀 2168

1樓:網友

深入淺出mfc-侯俊傑。pdf: 78頁:類與物件大解剖。

2樓:網友

這個就是典型的位元組對齊的問題了,不同的編譯器或作業系統的具體實現是不一樣的。

3樓:永遠愛你

這是因為在不同的編譯器中,相同的資料型別佔的位元組數不一樣而已。

c++虛表問題

4樓:洪水苼

虛擬函式表是編譯器用來實現多型的方法。一般來說,虛擬函式表是乙個陣列,陣列的元素是虛擬函式的指標。每個物件都會有乙個虛擬函式表,當在某個物件上呼叫虛擬函式時,通過查詢這個表來獲得繼承層次中到底哪個函式被實際呼叫。

對於乙個類如果有虛擬函式,就會在這個類中建立乙個虛表,也就會產生乙個虛指標指向這個虛表。

既然有乙個指標指向了虛表,這個類派生後,在派生類中就不必再建立虛表,如果派生類還有自己的虛擬函式,那麼只在派生類中建立該虛擬函式的乙個虛表,產生乙個指向該虛表的指標。

為每個類設定虛表,初始化虛指標,為虛擬函式呼叫插入**都是自動發生的,不必擔心這些。

我看到過虛繼承下虛表問題的分析,直接繼承下沒看過,特此又分析了一下,修改)

#include

using namespace std;

class a;};

class b:public virtual a;};

int main();

class b:public a;};

class c:public b;};

int main();

class b:public a;};

class c:public b;};

int main();

class b:public virtual a;};

class c:public virtual b;};

int main();

class b:public virtual a;};

class c:public virtual b;};

int main()

codeblocks下編譯執行結果為 4 4 4.

vc下編譯執行結果為:4 12 20.

從結果來看,vc下是 本類加父類後又加了4個位元組。而codeblocks結果就不再是本類加父類的結果(這個有點迷茫)。

c/c++編譯器中虛表是如何完成的?

5樓:網友

編譯器會蒐集乙個類的所有虛擬函式,並在編譯時生成乙個虛擬函式表。然後編譯器實際上會在類的構造和解構函式中加一些**來達到初始化虛表指標和改變虛表指標的目的。

6樓:

每乙個類都會有一張虛擬函式表的,當然了,這個類必須繼承了或者有虛擬函式。

並且這個虛擬函式表僅僅儲存的是函式的位址。

例如父類有兩個虛擬函式a和b,位址分別是0001和0002。此時父類的虛擬函式表就是8個位元組。

然後子類繼承了父類之後,子類也有乙個虛擬函式表。也是8個位元組。

如果子類沒有改寫父類的虛擬函式,那麼子類的虛擬函式表和父類的虛擬函式表是一樣的。

如果子類改寫了虛擬函式,那麼就發生變化了。例如子類修改了父類中的兩個虛擬函式,那麼子類中的虛擬函式表分別儲存了0003和0004兩個位址。

這就是虛擬函式的作用,如果你不修改父類的虛擬函式表,那麼你就是呼叫父類的虛擬函式。

如果你過載了,那麼就呼叫新的虛擬函式表中的位址來查詢過載之後的函式。

c++虛擬函式表的問題!(看一段**,高分懸賞)

7樓:竹林傾聽雨

首先我們應當清楚:

為了得到正確的虛擬函式偏移量,c++編譯器要將虛擬函式表的指標存在於物件例項中最前面的位置。

還有多層指標要從右往左一層一層撥剝開。

問題一:當然不一樣,首先p本身是一指標型別,是一種簡單型別,*&p:先得到p在實體記憶體中的位址,再取出該位址中的資料,這個資料依然是指標型別,例如:

p的實體地址是0x10,而0x10所在位置存的是0x20,那麼輸出就是0x20,而0x20依然是個位址。p:過程是相同的,只是寫起來比較簡單。

其次b是一物件型別,是非簡單型別,插入符「《無法識別,除非你寫乙個過載該運算子的函式。

b是取出物件的位址,(int*)(b)是將該位址轉換為整形指標,就是物件虛表陣列的首位址,*(int*)(b)是將取出虛表陣列的第一項,就是第乙個虛擬函式的首位址,(int*)*int*)(b)就是將該位址轉換為整形指標。

問題二:void*不是乙個指向物件型別的指標(int*,long*都是),所以你向他取東西肯定是不行的,例如:void* p = null;

cout <就不可以;問題三:你這個之所以可以 是因為,你的函式碰巧沒有引數,如果有的話,就不行了,編譯器會說類似 從void()**換成void()(char*)失敗。

而fun* 型別正好定義了引數序列。

8樓:河蟹和諧河蟹

(int*)*int*)(b)應該是把b的位址轉換成指標,然後用*或者指標指向的位置的值(即函式位址),最後通過(int*)轉換為整數指標列印。

void*有可能不是32位。我很久不用了,我也不能確定在裡void是否等於int長度,不過看一下錯誤資訊應該就知道是什麼問題了。

需要(fun)*實際上可以看成((*p)(void))和*兩部分不過這個問題比較難說清楚,自己試驗更能理解。

9樓:網友

第乙個問題:

乙個類建立的例項它的前4個位元組(即乙個指標的長度)就指向該函式的虛表,前提是該類有虛表。所以:

int*)*int*)(b),int*)(b),是先將物件的位址取出,轉換成int*,這樣就轉成了乙個int的指標。然後再用*取這個位址上的值,因為指標是int型別,所以就取出了4個位元組的值,再把這個值用(int*)轉成乙個int指標,這個指標就指向vtable了。這個vtable還是個指標,它的值才是第乙個函式的位址,所以還要再用*取次值。

用圖說明大概就是:

物件的前4個位元組 > vtable > 第乙個虛擬函式。

所以必須要有那個*。

第二個問題:

void是個空型別,沒有長度。而*是取位址上的值,必須要知道型別的長度,比如a是int*型別,那麼*a編譯器就知道從這個位址指向的位置上取4個位元組。雖然void*變數本身有長度,但編譯器無法知道其指向的位址有多長,所以用*取乙個void*型別位址的值在編譯時就會報錯。

void*指標一般配合強轉用作轉換成任何指標型別,不會也不能參與表示式運算。

第三個問題:

void fun();

這麼乙個函式,函式名代表函式入口位址,加個&還是函式位址,在表示式中返回的是存有該函式入口位址的函式指標,這是編譯器規定的。

像你寫的。void (*p)(void);

這個p就是乙個返回值為void,引數列表為void的函式指標,而fun返回乙個函式指標,跟p的型別匹配,所以不需要強轉。

為什麼光有int*轉換來的位址不能呼叫函式,因為作業系統呼叫乙個函式除了函式位址外,還需要它的引數列表,所以你光乙個位址是無法呼叫函式的。

這個時候就要用到函式指標。函式指標除了可以儲存函式位址外還能描述出函式的引數列表。

所以你用int*取到的位址,需要強轉成fun型別的函式指標,這樣編譯器就知道這個函式的引數和返回值,就能呼叫函式了。

關於c++虛擬函式的問題

10樓:手機使用者

虛擬函式可以實現多型性,但是隻有通過指標或者引用呼叫虛擬函式才會發生動態繫結,在你的程式main函式中:

dog d1;

animal a;

memcpy(&a, &d1, sizeof(dog));

eat函式是通過物件直接呼叫的,這裡不發生動態繫結,當然呼叫的是animal裡的eat函式了,你可以改動下:

dog d1;

animal *a=new animal;

memcpy(a, &d1, sizeof(dog));

a->eat();

試試,結果就對了。

11樓:網友

樓主啊。這是的bug。我用輸出「animal eat」

乙個c++虛擬函式與this指標的問題

12樓:網友

不可以。

是。是。你這個疑問就實際上涉及到了編譯技術。this指標你會發現他是c++的關鍵字,而不是在那個標頭檔案裡宣告的變數。也就是說,this指標並不是乙個真實存在的c++語言變數,「父類指標不能自動(隱式)轉換為子類指標」實際上是c++語言的變數型別規則,但this指標並非乙個變數,所以不需要遵循此規則。

從執行中的程式來說,this指標是幫助函式確定物件的位置。

void g(ia* a)

這段對應的組合語言重點如下:

mov rcx, [rsp+28h+arg_0] 《這裡就是把物件的首位址(就是變數a的值)放在rcx中。

call qword ptr [rax] 《呼叫f()函式。

virtual void f()

mov [rsp+arg_0], rcx <—將rcx(其實就是所謂this指標)壓進棧以防rcx暫存器有他用。

mov rax, [rsp+arg_0]

mov eax, [rax+0ch] 《通過this指標找到成員變數member的位置。

inc eax <—執行自增操作。

看到了吧,this指標不過就是類成員函式被呼叫時,被臨時儲存到rcx暫存器的物件首位址。

之所以囉嗦這麼多,意思就是讓你明白(如果你被上面所述搞得一頭霧水),從程式實際執行的角度上看,this指標與其說是c++的指標,還不如說是編譯技術裡的東西,彙編/機器語言里根本沒有型別一說(當然,機器最多分整數執行儲存單元和浮點數運算儲存單元),自然不存在什麼型別轉換了。

C 的虛擬函式有什麼用呢,C 中虛擬函式的作用是什麼?它應該怎麼用呢?

主要用在繼承抄與多型上 比如有一個襲汽車類,它有bai一個虛du函式alarm 就是鳴笛的聲zhi 音。汽車類有很多子 dao類,比如卡車類,小轎車類,他們的鳴笛聲音都一樣,所以只需繼承汽車類,不用在每個類裡重寫這個函式。現在如果新增一個卡丁車類,它的鳴笛聲音和其他類都不一樣,那就可以在這個類裡重寫...

c 中解構函式中可以呼叫虛擬函式麼

c 中 解構函式中不可以呼叫虛擬函式。effective c 中有這樣的描述 同樣的原因也適用於析構過程。一旦派生類解構函式執行,這個物件的派生類資料成員就被視為未定義的值,所以 c 就將它們視為不再存在。c 中派生類在構造時會先呼叫基類的建構函式再呼叫派生類的建構函式,析構時則相反,先呼叫派生類的...

虛擬函式和純虛擬函式的兩個基礎小問題

純虛擬函式就是 強636f707962616964757a686964616f31333262373361制 要求在派生出具體類的時候 必須定義出的函式,否則該派生類就無法定義具體物件 而虛擬函式則可重新定義也可以不重新定義 說得對。而僅僅站在使用的角度去說,虛擬函式和純虛擬函式都可以實現多型的功能...