[英]Why the size of a pointer to a function is different from the size of a pointer to a member function?
指針只是一個地址嗎? 或者我錯過了什么?
我測試了幾種類型的指針:
但是指向成員函數的指針更大 - 我的平台上有16B。
三件事:
void*
必須能夠“包含”任何指針類型。 換句話說,任何指針必須能夠被轉換為void*
,對吧? 如果是這樣,那么為什么sizeof( void* )
是8,而指向成員函數的指針的sizeof
是16? 編輯 :所以我注意到我在這幾個月后仍然得到了投票,盡管我的原始答案很糟糕且誤導(我甚至不記得我當時在想什么,而且它沒有多大意義!)所以我想我會嘗試澄清情況,因為人們仍然必須通過搜索來到這里。
在最正常的情況下,你幾乎可以想到
struct A {
int i;
int foo() { return i; }
};
A a;
a.foo();
如
struct A {
int i;
};
int A_foo( A* this ) { return this->i; };
A a;
A_foo(&a);
(開始看起來像C
,對嗎?)所以你會認為指針&A::foo
與普通函數指針一樣。 但是有一些復雜問題:多重繼承和虛函數。
所以想象我們有:
struct A {int a;};
struct B {int b;};
struct C : A, B {int c;};
它可能是這樣的:
如您所見,如果要指向具有A*
或C*
的對象,則指向開始,但如果要使用B*
指向它,則必須指向中間的某個位置。
因此,如果C
從B
繼承了一些成員函數並且你想指向它然后在C*
上調用該函數,它需要知道要this
指針進行洗牌。 這些信息需要存儲在某個地方。 所以它與函數指針混在一起。
現在,對於具有virtual
函數的每個類,編譯器都會創建一個名為虛擬表的列表 。 然后它將一個額外的指針添加到該類( vptr )。 所以對於這個類結構:
struct A
{
int a;
virtual void foo(){};
};
struct B : A
{
int b;
virtual void foo(){};
virtual void bar(){};
};
編譯器最終可能會像這樣:
因此,指向虛函數的成員函數指針實際上需要是虛擬表的索引。 因此,成員函數指針實際上需要1)可能是函數指針,2)可能調整this
指針,以及3)可能是vtable索引。 為了保持一致,每個成員函數指針都需要能夠滿足所有這些要求。 所以,這8
個字節的指針, 4
字節用於調整, 4
個字節的索引,對於16
字節總。
我相信這在編譯器之間實際上有很大不同,並且有很多可能的優化。 可能沒有人像我描述的那樣實際實現它。
對於很多的細節,請參見本 (滾動到“成員函數指針的實現”)。
這里可以找到一些解釋: 成員函數指針的底層表示
盡管指向成員的指針表現得像普通指針,但在幕后它們的表現形式卻截然不同。 事實上,指向成員的指針通常由一個結構組成,在某些情況下最多包含四個字段。 這是因為指向成員的指針不僅要支持普通的成員函數,還要支持虛擬成員函數,具有多個基類的對象的成員函數以及虛基類的成員函數。 因此,最簡單的成員函數可以表示為一組兩個指針:一個保存成員函數的物理內存地址,另一個指針保存該指針。 但是,在虛擬成員函數,多繼承和虛擬繼承的情況下,指向成員的指針必須存儲其他信息。 因此,您不能將指向成員的指針轉換為普通指針,也不能安全地在指向不同類型成員的指針之間進行轉換。 大段引用
我猜它與this
指針有關...也就是說,每個成員函數也必須有它們所在類的指針。然后指針使函數的大小更大一些。
將指向成員函數的指針表示為{this, T (*f)()}
一些主要原因是:
它在編譯器中的實現比實現成員函數的指針更簡單,如T (*f)()
它不涉及運行時代碼生成,也不涉及額外的簿記
與T (*f)()
相比,它表現得相當好
C ++程序員對成員函數指針的大小等於sizeof(void*)
要求不夠
執行期間運行時代碼生成事實上是目前C ++代碼的禁忌
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.