2012-05-25 14 views
17

Considere este archivo, first.cpp, que contiene una definición de clase y uso:¿Por qué el enlazador ld permite múltiples definiciones de clase con los mismos métodos?

#include <iostream> 

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

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

y otro, second.cpp, que contiene una definición de clase en conflicto:

#include <iostream> 

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

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

El enlazador se queja de símbolos duplicados cuando hay dos funciones con los mismos nombres definidos, pero estos archivos con métodos de clase duplicados se compilan sin un error.

I compilado con estos comandos:

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

Reordenación de los argumentos para la segunda g++ llamada no cambia la salida.

Y cuando first se ejecuta, esta es la salida:

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

¿Por qué el enlazador permiten métodos de clase duplicados? Si aparentemente está permitido, ¿por qué se imprime wrong ~Foo()?

+0

Creo que depende de la versión del compilador, pero lleva el primero que encuentre. – Brady

+0

Es GCC 4.6.1. –

+3

Probablemente tenga algo que ver con la función en línea que da paso a una función de archivo de objeto donde está presente. Supongo que tendrías el mismo problema con el constructor si declaraste una versión no en línea en second.cpp y el problema desaparecería si ambas fuentes declararan las funciones en línea. – forsvarir

Respuesta

14

De nuevo, Comportamiento indefinido. Su programa tiene múltiples definiciones para el destructor de Foo, lo que significa que está en violación de la ODR. El programa está mal y todo puede pasar.

¿Por qué el enlazador no lo recoge? Cuando se define una función dentro de la definición de clase, implícitamente es inline. Los compiladores generalmente marcan esas funciones como 'símbolos débiles'. El vinculador obtiene todas las unidades de traducción e intenta resolver los símbolos. El enlazador eliminará los símbolos débiles si es necesario (es decir, si el símbolo ya está definido en otro lugar).

A partir de la salida real del programa, parece que el compilador en realidad no inline la llamada al constructor y por lo tanto enviados en tiempo de ejecución al símbolo que fue dejado por el enlazador (la no débil)


¿Por qué el vinculador permite tener métodos duplicados?

Debido a que todos (pero como máximo uno) son símbolos débiles (es decir inline)

Por qué, en este caso, errónea ~ Foo() se imprime?

Debido a que la llamada no se inline, y el enlazador se redujo el símbolo débil

+1

Por alguna razón, ver ** Comportamiento Indefinido ** en negrita me pone mareado. –

+0

@ChetSimpson: Mi mal, esta pregunta es muy similar a la de hoy en día, donde la respuesta fue que es UB. Siendo casi lo mismo, asumí erróneamente que la misma persona estaba insistiendo en caminar al borde de la hemorragia, pero en retrospectiva parece que las dos preguntas fueron hechas por diferentes personas. –

+0

@@ david-rodriguez-dribeas Todo está bien, UB necesita ser audaz de vez en cuando;) –

Cuestiones relacionadas