2012-05-25 12 views
17

Betrachten Sie diese Datei, first.cpp, eine Klassendefinition und Verwendung enthalten:Warum erlaubt der ld-Linker mehrere Klassendefinitionen mit denselben Methoden?

#include <iostream> 

struct Foo 
{ 
    Foo(){ std::cout << "Foo()" << std::endl; } 
    ~Foo(){ std::cout << "~Foo()" << std::endl; } 
}; 

int main(){ 
    Foo f; 
    return 0; 
} 

und eine andere, second.cpp, eine widersprüchliche Klassendefinition enthalten:

#include <iostream> 

struct Foo 
{ 
    Foo(); 
    ~Foo(); 
}; 

Foo::~Foo(){ std::cout << "wrong ~Foo()" << std::endl; } 

Der Linker über doppelte Symbole beschwert sich, wenn es zwei sind Funktionen mit den gleichen Namen definiert, aber diese Dateien mit doppelten Klassenmethoden kompilieren ohne einen Fehler.

ich mit diesen Befehlen zusammengestellt:

$ g++ -c second.cpp -o second 
$ g++ second first.cpp -o first 

Neuordnen die Argumente der zweiten g++ Aufruf ändert nicht den Ausgang.

Und wenn first ausgeführt wird, ist dies die Ausgabe:

$ ./first 
Foo() 
wrong ~Foo() 

Warum der Linker erlauben doppelte Klassenmethoden? Wenn es anscheinend erlaubt ist, warum ist wrong ~Foo() gedruckt?

+0

Ich denke, es ist an der Version des Compilers hängt, aber es nimmt die ersten gefundenen. – Brady

+0

Es ist GCC 4.6.1. –

+3

Es hat wahrscheinlich etwas mit der Funktion zu tun, die einer Objektdateifunktion Platz macht, wo sie vorhanden ist. Ich nehme an, Sie hätten das gleiche Problem mit dem Konstruktor, wenn Sie eine non-inline-Version in second.cpp deklarierten und das Problem wegfallen würde, wenn beide Quellen die Funktionen inline deklarierten. – forsvarir

Antwort

14

Noch einmal, undefiniertes Verhalten. Ihr Programm hat mehrere Definitionen für den Destruktor von Foo, was bedeutet, dass es gegen ODR verstößt. Das Programm ist falsch und alles kann passieren.

Warum nimmt der Linker es nicht auf? Wenn eine Funktion innerhalb der Klassendefinition definiert ist, ist dies implizit inline. Compiler markieren diese Funktionen normalerweise als "schwache Symbole". Der Linker ruft dann alle Übersetzungseinheiten ab und versucht, die Symbole aufzulösen. Schwache Symbole werden bei Bedarf vom Linker gelöscht (d. H. Wenn das Symbol bereits irgendwo anders definiert ist).

Wie die tatsächlichen Ausgabe des Programms sieht es aus wie der Compiler tatsächlich nicht den Aufruf an den Konstruktor Inline- und somit zur Laufzeit auf das Symbol versandt, die vom Linker verlassen wurde (die nicht-schwache)

Warum Linker ermöglicht doppelte Methoden zu haben?

Da alle (aber höchstens eine) sind schwache Symbole (d inline)

Warum, in diesem Fall falsch ~ Foo() gedruckt wird?

Da der Anruf nicht inlined wurde, und der Linker ließ das schwache Symbol

+1

Aus irgendeinem Grund ** nicht definiertes Verhalten ** fett gedruckt zu sehen, macht mich ganz schwindlig. –

+0

@ChetSimpson: Meine schlecht, diese Frage ist sehr ähnlich zu einem anderen von heute, wo die Antwort war, dass es UB ist. Dies ist fast das gleiche, ich nahm fälschlicherweise, dass die gleiche Person war das Beharren auf den bleeding edge zu Fuß, aber im Nachhinein scheint es, dass die beiden Fragen, die von verschiedenen Personen befragt wurden. –

+0

@@ david-rodriguez-dribeas Es ist alles gut, braucht UB hin und wieder fett zu sein;) –

Verwandte Themen