2012-05-25 8 views
17

Rozważmy następujący plik first.cpp, zawierający definicję klasy i zastosowanie:Dlaczego łącznik LD dopuszcza wiele definicji klas za pomocą tych samych metod?

#include <iostream> 

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

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

a inny, second.cpp, zawierający konflikt definicję klasy:

#include <iostream> 

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

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

Łącznik narzeka na zduplikowane symboli, gdy istnieją dwa funkcje o tych samych nazwach, ale te pliki z powielonymi metodami klasy kompilują się bezbłędnie.

skompilowany z tych poleceń:

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

Zmiana kolejności argumenty do drugiego g++ zaproszenia nie zmienia wyjście.

A kiedy first jest prowadzony, jest to wyjście:

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

Dlaczego łącznik pozwalają zduplikowane metod klasy? Jeśli jest to dozwolone, dlaczego wydrukowano wrong ~Foo()?

+0

myślę, że to zależy od wersji kompilatora, ale to trwa pierwszy z nich znajdzie. – Brady

+0

To GCC 4.6.1. –

+3

To prawdopodobnie ma coś wspólnego z funkcją wstawiania ustępującą funkcji pliku obiektowego, w której jest obecna. Domyślam się, że masz taki sam problem z konstruktorem, jeśli zadeklarowałeś wersję nieinsertowaną w second.cpp i problem zniknąłby, gdyby oba źródła zadeklarowały funkcje inline. – forsvarir

Odpowiedz

14

Ponownie, niezdefiniowane zachowanie. Twój program ma wiele definicji dla destruktora z Foo, co oznacza, że ​​narusza ODR. Program jest zły i wszystko może się zdarzyć.

Dlaczego linker go nie odbiera? Gdy funkcja jest zdefiniowana w definicji klasy, jest domyślnie inline. Kompilatory zazwyczaj oznaczają te funkcje jako "słabe symbole". Łącznik następnie pobiera wszystkie jednostki tłumaczeniowe i próbuje rozwiązać symbole. Słabe symbole zostaną usunięte przez linker, jeśli zajdzie taka potrzeba (to znaczy, jeżeli symbol jest już zdefiniowany gdzie indziej).

Jak rzeczywistej wydajności programu, wygląda na to kompilator nie faktycznie inline wywołanie konstruktora, a zatem wysyłane w czasie wykonywania do symbolu, który został pozostawiony przez łącznik (non-słaby)


Dlaczego linker pozwala na powielanie metod?

Ponieważ wszystkie (ale co najwyżej jeden) są słabe symbole (tzn inline)

Dlaczego w tym przypadku źle ~ Foo() jest drukowany?

Ponieważ połączenie nie zostało inlined, a łącznik upuścił słaby symbol

+1

Z jakiegoś powodu widzenie ** Niezdefiniowane zachowanie ** pogrubioną sprawia, że ​​jestem oszołomiony. –

+0

@ChetSimpson: Moja zła, to pytanie jest bardzo podobne do tego z dzisiejszego, gdzie odpowiedź brzmiała, że ​​to UB. To prawie to samo, błędnie założyłem, że ta sama osoba nalegała na chodzenie po krwawiących krawędziach, ale z perspektywy czasu wydaje się, że dwa pytania zadawali różni ludzie. –

+0

@@ david-rodriguez-dribeas Wszystko dobrze, UB musi być odważne co jakiś czas;) –

Powiązane problemy