簡體   English   中英

為什么指向函數的指針大小與指向成員函數的指針大小不同?

[英]Why the size of a pointer to a function is different from the size of a pointer to a member function?

指針只是一個地址嗎? 或者我錯過了什么?

我測試了幾種類型的指針:

  • 指向任何變量的指針是相同的(我平台上的8B)
  • 指向函數的指針大小相同,作為指向變量的指針(8B再次)
  • 指向具有不同參數的函數的指針 - 仍然相同(8B)

但是指向成員函數的指針更大 - 我的平台上有16B。

三件事:

  1. 為什么指向成員函數的指針更大? 他們需要更多信息?
  2. 據我所知, 標准沒有說明指針的大小, 除了 void*必須能夠“包含”任何指針類型。 換句話說,任何指針必須能夠被轉換為void* ,對吧? 如果是這樣,那么為什么sizeof( void* )是8,而指向成員函數的指針的sizeof是16?
  3. 是否還有其他指針示例,它們具有不同的大小(我的意思是,對於標准平台,不是一些稀有和特殊的平台)?

編輯 :所以我注意到我在這幾個月后仍然得到了投票,盡管我的原始答案很糟糕且誤導(我甚至不記得我當時在想什么,而且它沒有多大意義!)所以我想我會嘗試澄清情況,因為人們仍然必須通過搜索來到這里。

在最正常的情況下,你幾乎可以想到

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*指向它,則必須指向中間的某個位置。

因此,如果CB繼承了一些成員函數並且你想指向它然后在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字節總。

我相信這在編譯器之間實際上有很大不同,並且有很多可能的優化。 可能沒有人像我描述的那樣實際實現它。

對於很多的細節,請參見 (滾動到“成員函數指針的實現”)。

基本上是因為他們需要支持多態行為。 看看Raymond Chen寫的一篇文章

這里可以找到一些解釋: 成員函數指針的底層表示

盡管指向成員的指針表現得像普通指針,但在幕后它們的表現形式卻截然不同。 事實上,指向成員的指針通常由一個結構組成,在某些情況下最多包含四個字段。 這是因為指向成員的指針不僅要支持普通的成員函數,還要支持虛擬成員函數,具有多個基類的對象的成員函數以及虛基類的成員函數。 因此,最簡單的成員函數可以表示為一組兩個指針:一個保存成員函數的物理內存地址,另一個指針保存該指針。 但是,在虛擬成員函數,多繼承和虛擬繼承的情況下,指向成員的指針必須存儲其他信息。 因此,您不能將指向成員的指針轉換為普通指針,也不能安全地在指向不同類型成員的指針之間進行轉換。 大段引用

我猜它與this指針有關...也就是說,每個成員函數也必須有它們所在類的指針。然后指針使函數的大小更大一些。

將指向成員函數的指針表示為{this, T (*f)()}一些主要原因是:

  • 它在編譯器中的實現比實現成員函數的指針更簡單,如T (*f)()

  • 它不涉及運行時代碼生成,也不涉及額外的簿記

  • T (*f)()相比,它表現得相當好

  • C ++程序員對成員函數指針的大小等於sizeof(void*)要求不夠

  • 執行期間運行時代碼生成事實上是目前C ++代碼的禁忌

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM