2012-05-25 10 views
17

Considerate questo file, first.cpp, contenente una definizione di classe ed impiego:Perché il linker ld consente più definizioni di classe con gli stessi metodi?

#include <iostream> 

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

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

e un altro, second.cpp, contenente una definizione di classe in conflitto:

#include <iostream> 

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

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

Il linker lamenta simboli duplicati quando ci sono due funzioni con gli stessi nomi definiti, ma questi file con metodi di classe duplicati vengono compilati senza errori.

ho compilato con questi comandi:

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

Riordino gli argomenti alla seconda g++ chiamata non cambia l'uscita.

E quando first viene eseguito, questo è l'output:

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

Perché il linker consentire metodi di classe duplicati? Se è apparentemente consentito, perché è stampato wrong ~Foo()?

+0

Penso che dipende dalla versione del compilatore, ma ci vuole il primo che trova. – Brady

+0

È GCC 4.6.1. –

+3

Probabilmente ha qualcosa a che fare con la funzione di inlining che dà il via ad una funzione di file oggetto dove è presente. Immagino che avresti lo stesso problema con il costruttore se hai dichiarato una versione non inline in second.cpp e il problema scomparirebbe se entrambe le fonti dichiarassero le funzioni in linea. – forsvarir

risposta

14

Ancora, comportamento non definito. Il tuo programma ha più definizioni per il distruttore di Foo, il che significa che è in violazione dell'ODR. Il programma è sbagliato e tutto può accadere.

Perché il linker non lo preleva? Quando una funzione è definita all'interno della definizione della classe, è implicitamente inline. I compilatori di solito contrassegnano quelle funzioni come "simboli deboli". Il linker ottiene quindi tutte le unità di traduzione e tenta di risolvere i simboli. I simboli deboli verranno eliminati dal linker se necessario (ad esempio se il simbolo è già definito da qualche altra parte).

Come dell'uscita effettiva del programma, sembra che il compilatore in realtà non inline la chiamata al costruttore e quindi spedito in fase di esecuzione al simbolo che è stato lasciato dal linker (quello non debole)


Perché il linker consente di avere metodi duplicati?

Perché tutti (ma al massimo uno) sono simboli deboli (cioè inline)

Perché, in questo caso, sbagliato ~ Foo() viene stampato?

Perché la chiamata non è stata inline, e il linker cadere il simbolo deboli

+1

Per qualche ragione vedere ** Comportamento indefinito ** in grassetto mi rende tutti vertiginoso. –

+0

@ChetSimpson: Il mio male, questa domanda è molto simile a un'altra di oggi in cui la risposta è che è UB. Essendo quasi la stessa cosa, ho erroneamente presupposto che la stessa persona stesse insistendo nel camminare sul bordo sanguinante, ma a ben vedere sembra che le due domande siano state poste da persone diverse. –

+0

@@ david-rodriguez-dribeas Va tutto bene, UB deve essere audace di tanto in tanto;) –

Problemi correlati