Domanda

Sono abituato a pensare alle funzioni membro come ad essere solo un caso speciale di funzioni normali, in cui le funzioni membro hanno un parametro aggiuntivo all'inizio del loro elenco di parametri per il puntatore "questo", ovvero l'oggetto su cui la funzione membro dovrebbe agire. Ho usato boost :: function in questo modo in passato e non ho mai riscontrato problemi:

boost::function f<(void)(MyObject*, int, int)> = &MyObject::method_that_takes_two_ints;

Ma ho visto questa sintassi per i puntatori della funzione membro:

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

In questa sintassi, il parametro 'this' non è visibile. Il che mi ha fatto domandare se sotto il cofano le funzioni da puntatore a membro sono davvero una bestia separata, e quella spinta si è presa cura dei dettagli per me.

Cosa impone lo standard sul posizionamento del parametro 'this'? Forse solo sul mio compilatore l'argomento extra "questo" viene prima, e forse su altri compilatori potrebbe essere alla fine? Sono solo fortunato che il mio modo di pensare sia coerente con il modo in cui i miei compilatori (GCC4, VS2005) lo gestiscono? Le funzioni da puntatore a membro sono sempre solo un caso speciale di funzioni da puntatore a con un parametro aggiuntivo o il compilatore può implementarle diversamente?

È stato utile?

Soluzione

Lo standard non dice quasi nulla su dove dovrebbe essere collocato il puntatore questo , ed infatti è abbastanza comune usare una diversa convenzione di chiamata per le funzioni membro. (Quindi il puntatore "this" non è solo un primo argomento in più, in realtà è memorizzato in una posizione diversa rispetto al solito arg primo)

In particolare, MSVC utilizza la convenzione di chiamata thiscall per le funzioni membro e stdcall altrove. http://www.hackcraft.net/cpp/MSCallingConventions/#thiscall descrive le differenze tra loro, ma nota che thiscall memorizza il puntatore this nel registro ECX , mentre stdcall archivia < em> tutti i parametri nello stack.

Stai decisamente meglio trattandoli come tipi completamente distinti. Un puntatore a una funzione membro è non solo un puntatore a una funzione con un parametro aggiuntivo.

Altri suggerimenti

Il puntatore this non è memorizzato lungo un puntatore al membro (i puntatori di funzione membro ne sono un caso speciale). Se lo fai

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

quindi ciò che viene memorizzato sono solo le informazioni su quale funzione membro dovrebbe essere chiamata su un oggetto che successivamente dovrai fornire. Se vuoi chiamarlo, devi passare un oggetto, da dove il compilatore otterrà il puntatore this .

MyObject o; (o.*f)(1, 2);

Un puntatore a funzione membro è solo un puntatore membro il cui tipo (che è indicato) è un tipo di funzione. Lo standard afferma che i puntatori a funzioni membro non hanno il proprio tipo di funzione "quot" membro che indicano e che in qualche modo includerebbe questo tipo di puntatore.

int main() {
    typedef void fun() const;
    fun MyObject::*mem_function_ptr = 
        &MyObject::const_method_that_takes_two_ints;
}

fun in quel codice è il tipo di funzione. Il tipo che un "normale" la funzione ha. Un puntatore a funzione, al contrario di un membro-funzione-puntatore, è solo un puntatore a una funzione con quel tipo:

void foo() { cout << "hello"; }
int main() {
    typedef void fun();
    fun * f = &foo;
}

Mentre una funzione da puntatore a membro ha il livello aggiuntivo di puntatore membro in cima a quel tipo di funzione.

Qualcosa sul puntatore this e su come si riferisce all'oggetto a cui punta (non tecnico, solo materiale teorico):

Ogni funzione membro ha un parametro nascosto chiamato parametro oggetto implicito che ha il tipo MyObject & amp; o MyObject const & amp; a seconda che tu abbia una funzione membro const o nonconst. L'oggetto su cui si chiama la funzione membro, o , è argomento dell'oggetto implicito , che viene passato al parametro. Nella teoria dello standard che costituisce le regole che descrivono come vengono chiamate le funzioni membro, il parametro oggetto implicito è un primo parametro nascosto. È concettuale e non significa che sia il caso reale delle implementazioni. L'argomento oggetto implicito viene quindi associato a quel parametro oggetto implicito, probabilmente causando conversioni implicite (quindi se si chiama una funzione membro const su un oggetto non const, una conversione di qualifica viene convertita da MyObject a MyObject const & amp; . Questo è ciò che rende le funzioni non const una scelta migliore rispetto alle funzioni const da chiamare, per un oggetto non const). Ad esempio, si può dire in questo codice:

struct A {
    operator int() const { return 0; }
};

int main() { 
    A a;
    int i = a; // implicit conversion using the conversion function
}

Che l'argomento oggetto implicito a di tipo A è associato al parametro oggetto implicito di tipo A const & amp; , il cui oggetto è quindi indicato dal puntatore this con il tipo A const * qui. È importante notare che il parametro oggetto implicito è solo un costrutto teorico, per formalizzare il modo in cui sono costituite le regole per chiamare una funzione membro (e i costruttori non le includono), mentre questo puntatore è effettivamente esistente. this è un puntatore, perché quando this è stato introdotto, C ++ non aveva ancora riferimenti.

Spero che ti possa aiutare a capire la questione.

Sì, i puntatori alle funzioni e i puntatori ai membri sono animali completamente diversi. Ai puntatori ai membri deve essere fornita un'istanza di oggetto per essere notificati mediante gli operatori - > * o . * . Non è stato utilizzato alcun parametro this quando si crea un puntatore a membro poiché this viene determinato quando viene utilizzato il puntatore a membro (l'oggetto a sinistra di - > * o . * ).

Nota che probabilmente c'è meno differenza tra una funzione da puntatore a membro e una variabile da puntatore a membro di quella tra una funzione da puntatore a membro e un normale puntatore a funzione.

In genere le funzioni membro e le funzioni regolari possono avere convenzioni di chiamata completamente diverse, quindi non è possibile eseguire il cast tra di esse.

Sono sicuramente tipi distinti e qualsiasi ipotesi che farai sarà specifica per piattaforma / compilatore.

Questa pagina contiene più informazioni sull'implementazione dei punti di funzione dei membri rispetto a Ho sempre desiderato saperlo, compresi i dettagli di implementazione di numerosi compilatori popolari.

Per rispondere a tutte le domande: Sì, sono puntatori speciali, diversi dai normali puntatori. Sì, boost :: function li riconosce.

Lo standard non dice nulla sui dettagli interni degli stack di chiamate. In effetti, molti compilatori possono utilizzare numeri interi, registri a virgola mobile e / o lo stack a seconda dell'elenco di argomenti effettivo. Un puntatore "questo" è solo un altro caso speciale.

Boost :: function risolve questo problema usando due percorsi di codice internamente. Puoi vederlo controllando lo stack di chiamate per i due casi. Se la funzione boost :: memorizza un puntatore alla funzione membro, l'operatore () suddividerà l'elenco degli argomenti. Il primo argomento viene utilizzato come oggetto su cui viene chiamata la funzione membro con gli argomenti rimanenti.

Per integrare la risposta di tutti gli altri, Boost.Function lavora specializzando l'operatore di assegnazione sui puntatori di funzione membro, per consentirgli di rilevare quando ne hai superato uno. Quando chiamate quella funzione, la reinterpreterà internamente al metodo corretto di chiamare il puntatore alla funzione membro ( (obj- > * fun) (args) ).

Penso che potresti trovare questo link abbastanza interessante:

http://www.parashift.com/c++ -FAQ-Lite / puntatori-a-members.html

È un'ottima descrizione di tutto ciò che vorresti sapere sui puntatori ai membri.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top