2009-10-04 8 views
6

Als ich litb answer to this question gelesen habe, habe ich gelernt, dass das Übergeben eines Arrays durch Referenz uns ermöglicht, seine Größe zu erhalten. Ich spielte nur wenig mit Code und versuchte, eine „Funktion“ durch Verweis übergeben und überraschend (zumindest für mich), dieser Code kompiliert:Könnte jemand bitte den Unterschied zwischen einer "Referenz" und einem "Zeiger" in diesem Fall erklären?

void execute(void (&func)()) // func is passed by reference! 
{ 
    func(); 
} 

Gibt es einen Unterschied zwischen der letzten Funktion, und dieser :

Ich versuchte es mit VC2008, und es produziert in jedem Fall unterschiedliche Ausgabe. Das Merkwürdige ist, dass der Compiler den Code besser im Fall eines Funktionszeigers optimiert:

void print() 
{ 
    std::cout << "Hello References!"; 
} 
void execute(void (&func)()) // optimized 
{ 
    func(); 
} 
int main() 
{ 
    00291020 call print (291000h) 
} 
========================================= 
// In this case, the compiler removes all function calls in the code! 
void print() // optimized! 
{ 
    std::cout << "Hello Pointers!"; 
} 
void execute(void (*func)()) // optimized 
{ 
    func(); 
} 
int main() 
{ 
    002F1005 push offset string "Hello References!" (2F2124h) 
    002F100A push eax 
    002F100B call std::operator<<<std::char_traits<char> > (2F1150h) 
} 

Es muss ein Unterschied sein, obwohl ich es nicht sehen, nicht wahr?

Hinweis: Der Code wurde unter Verwendung von VC2008 erstellt, mit /O2 und /Ot eingeschaltet.


EDIT :: Ich bin wirklich daran interessiert, über jeden Unterschied zwischen Funktionsreferenzen und Funktionszeiger. Ich habe den produzierten Assembler-Code untersucht, nur um zu sehen, wie er in jedem Fall übersetzt wird.

+0

sind Sie nur Interesse an dem Optimizern Verhalten oder in allgemeine Funktionsbezugsinformation? –

+0

@litb Ich bin eigentlich überrascht, dass ich eine Funktion per Referenz übergeben konnte. Es wäre großartig, wenn Sie eine Erklärung über den Unterschied in der Funktionalität oder andere Aspekte von Funktionszeigern haben :) – AraK

Antwort

3

Für die Sprachdifferenz (im Folgenden nur die Funktionsdeklarationen zu halten, denn das ist, was nur wichtig ist)

void execute(void (&func)()); 

void g(); 
int main() { 
    void (*fp)() = g; 
    execute(fp); // doesn't work 
    execute(&g); // doesn't work either 
    execute(g); // works 
} 

Es funktioniert nicht, da es eine Funktion will, keinen Funktionszeiger. Aus dem gleichen Grund, in dem die Array-Antwort einen Zeiger zurückweist, weist dies auch einen Zeiger zurück. Sie müssen "g" direkt übergeben.

Für Vorlagen, es zählt zu

template<typename T> 
void execute(T &t) { T u = t; u(); } 

template<typename T> 
void execute(T t) { T u = t; u(); } 

Diese beiden voneinander sehr verschieden sind. Wenn Sie es wie oben beschrieben mit execute(g); aufrufen, versucht der erste, eine Funktion zu deklarieren und initialisiert sie mit t (Referenz auf g). Die erzeugte Funktion würde so aussehen

void execute(void(&t)()) { void u() = t; u(); } 

Jetzt können Sie Referenzen und Zeiger auf Funktionen initialisieren, aber natürlich nicht Funktionen selbst. In der zweiten Definition wird T zu einem Funktionszeigertyp durch Vorlagenargumentableitung abgeleitet, und das Übergeben einer Funktion wird diesen implizit in diesen Zeigerparametertyp umwandeln. So wird alles gut gehen.


Ich weiß nicht, warum sie anders MSVC für inlining behandelt - aber ich vermute auch, es ist, weil Funktion Referenzen seltener erscheinen.

+0

Was ist mit 'execute (* fp);', obwohl - der fehlende vierte Fall :-) –

3

Es ist nicht so üblich ein Idiom, also könnte es sein, dass das VS-Team keine Regel hinzugefügt hat, um es zu optimieren.

3

Ich denke, es ist an den C++ Standard-gebührt 4.3:

ein L-Wert von Funktionstyp T mit einem R-Wert vom Typ „Zeiger auf T“ umgewandelt werden, kann das Ergebnis ein Zeiger auf ist die Funktion .

+0

Interessantes Zitat. Bedeutet das nicht, dass nach der Konvertierung der Funktionsreferenz grundsätzlich der gleiche Code in beiden Fällen erzeugt werden sollte. Ich weiß, dass dies ein Implementierungsdetail ist, aber wie könnte der Compiler den Zeiger und nicht die Referenz optimieren (wenn sie konvertiert wird)? – AraK

-2

Der Unterschied zwischen einem Verweis (&) und einem Zeiger (*) besteht darin, dass der Verweis die Adresse der Variablen oder den Speicherort angibt und der Zeiger auf den Speicherort der darin gespeicherten Adresse zeigt.

int *pointer; 
int variable; 

pointer = &variable; // assigning the address of variable to pointer 

variable = 53; // value of variable 

cout << *pointer; // This should output the value of the address where is pointing, in this 
        // case 53, that is the value of variable to where is pointing. 

Wir können feststellen, dass die (& Variable) die Adresse des Speicherplatzes und * anyname zeigt auf die Adresse in seinem Speicher gespeichert ...

+0

_ "die Referenz liefert die Adresse der Variablen oder der Ort, und der Zeiger zeigt auf den Speicherort der gespeicherten Adresse im Speicher darin. "_ huh ... Ich denke, du verwechselst Referenzen für die Adresse des Betreibers. –

Verwandte Themen