Perché la dimensione di un puntatore a una funzione è diversa dalla dimensione di un puntatore a una funzione membro?

Perché la dimensione di un puntatore a una funzione è diversa dalla dimensione di un puntatore a una funzione membro?

MODIFICA :Quindi ho notato che sto ancora ricevendo voti su questo mesi dopo, anche se la mia risposta originale è pessima e fuorviante (non riesco nemmeno a ricordare cosa stavo pensando in quel momento, e non ha molto senso! ) quindi ho pensato di provare a chiarire la situazione, poiché le persone devono ancora arrivare qui tramite la ricerca.

Nella situazione più normale, puoi praticamente pensare a

struct A {
    int i;
    int foo() { return i; }
};

A a;
a.foo();

come

struct A {
    int i;
};
int A_foo( A* this ) { return this->i; };

A a;
A_foo(&a);

(Iniziando a sembrare C , giusto?) Quindi penseresti il ​​puntatore &A::foo sarebbe solo lo stesso di un normale puntatore a funzione. Ma ci sono un paio di complicazioni:eredità multipla e funzioni virtuali.

Quindi immagina di avere:

struct A {int a;};
struct B {int b;};
struct C : A, B {int c;};

Potrebbe essere strutturato in questo modo:

Come puoi vedere, se vuoi puntare all'oggetto con un A* o un C* , indichi l'inizio, ma se vuoi puntarlo con un B* devi puntare da qualche parte nel mezzo.

Quindi se C eredita alcune funzioni membro da B e vuoi puntarlo, quindi chiama la funzione su un C* , deve sapere per mescolare this puntatore. Quelle informazioni devono essere archiviate da qualche parte. Quindi viene raggruppato con il puntatore alla funzione.

Ora per ogni classe che ha virtual funzioni, il compilatore ne crea un elenco chiamato tabella virtuale . Quindi aggiunge un puntatore extra a questa tabella alla classe (vptr ). Quindi per questa struttura di classe:

struct A
{
    int a;
    virtual void foo(){};
};
struct B : A
{
    int b;
    virtual void foo(){};
    virtual void bar(){};
};

Il compilatore potrebbe finire per farlo in questo modo:

Quindi un puntatore a funzione membro a una funzione virtuale deve effettivamente essere un indice nella tabella virtuale. Quindi un puntatore a funzione membro ha effettivamente bisogno di 1) possibilmente un puntatore a funzione, 2) possibilmente una regolazione del this pointer e 3) possibilmente un indice vtable. Per essere coerente, ogni puntatore a funzione membro deve essere in grado di eseguire tutte queste operazioni. Quindi è 8 byte per il puntatore, 4 byte per la regolazione, 4 byte per l'indice, per 16 byte totali.

Credo che questo sia qualcosa che in realtà varia molto tra i compilatori e ci sono molte possibili ottimizzazioni. Probabilmente nessuno lo implementa effettivamente nel modo che ho descritto.

Per un lotto per i dettagli, vedere questo (scorrere fino a "Implementazioni dei puntatori alle funzioni dei membri").


Fondamentalmente perché devono supportare il comportamento polimorfico. Vedi un bell'articolo di Raymond Chen.


Alcune spiegazioni possono essere trovate qui:La rappresentazione sottostante dei puntatori alle funzioni membro