c++ - otro - incluir header en c
error de definición múltiple que incluye el archivo de encabezado c++ con código en línea de múltiples fuentes (6)
Su primer fragmento de código no cumple con la "Regla de una sola definición" de C ++ . Consulte aquí un enlace a un artículo de Wikipedia que describe ODR. De hecho, no está del punto 2 porque cada vez que el compilador incluye el archivo de encabezado en un archivo fuente, se corre el riesgo de que el compilador genere una definición globalmente visible de test_ns::TestClass::testMethod()
. Y, por supuesto, cuando logre vincular el código, el vinculador tendrá gatitos, ya que encontrará el mismo símbolo en varios archivos de objetos.
El segundo fragmento funciona porque ha introducido la definición de la función, lo que significa que incluso si el compilador no genera ningún código en línea para la función (por ejemplo, tiene desactivada la alineación o el compilador decide que la función también es grande a en línea), el código generado para la definición de la función será visible solo en la unidad de traducción, como si lo hubiera pegado en un espacio de nombre anónimo. Por lo tanto, obtiene múltiples copias de la función en el código objeto generado que el vinculador puede optimizar o no según lo inteligente que sea.
Puede lograr un efecto similar en su primer fragmento de código al TestClass::testMethod()
prefijo TestClass::testMethod()
con inline
.
Tengo un archivo de encabezado c ++ que contiene una clase. Quiero usar esta clase en varios proyectos, pero no quiero crear una biblioteca separada para él, así que estoy poniendo declaraciones de métodos y definiciones en el archivo de encabezado:
// example.h
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
namespace test_ns{
class TestClass{
public:
void testMethod();
};
void TestClass::testMethod(){
// some code here...
}
} // end namespace test_ns
#endif
Si dentro del mismo proyecto multiple definition of test_ns::TestClass::testMethod()
este encabezado de más de un archivo cpp, multiple definition of test_ns::TestClass::testMethod()
un error que dice " multiple definition of test_ns::TestClass::testMethod()
", mientras que si pongo la definición del método dentro del cuerpo de la clase esto no ocurrir:
// example.h
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
namespace test_ns{
class TestClass{
public:
void testMethod(){
// some code here...
}
};
} // end namespace test_ns
#endif
Dado que la clase se define dentro de un espacio de nombres, ¿no deberían las dos formas ser equivalentes? ¿Por qué se considera que el método se definió dos veces en el primer caso?
Estos no son equivalentes. El segundo ejemplo dado tiene un modificador "en línea" implícito en el método y, por lo tanto, el compilador reconciliará varias definiciones (lo más probable es que tenga un enlace interno del método si no es inlineable).
El primer ejemplo no está en línea y, por lo tanto, si este encabezado se incluye en varias unidades de traducción, tendrá múltiples definiciones y errores de enlazador.
Además, los encabezados siempre deben estar protegidos para evitar múltiples errores de definición en la misma unidad de traducción. Eso debería convertir tu encabezado en:
#ifndef EXAMPLE_H
#define EXAMPLE_H
//define your class here
#endif
No coloque una definición de función / método en un archivo de encabezado a menos que estén en línea (definiéndolos directamente en una declaración de clase o explicidad especificada por la palabra clave inline)
los archivos de encabezado son (principalmente) para la declaración (lo que necesite declarar). Las definiciones permitidas son las de constantes y funciones / métodos en línea (y también plantillas).
//Baseclass.h or .cpp
#ifndef CDerivedclass
#include "Derivedclass.h"
#endif
or
//COthercls.h or .cpp
#ifndef CCommonheadercls
#include "Commonheadercls.h"
#endif
I think this suffice all instances.
Dentro del cuerpo de la clase se considera que está en línea por el compilador. Si implementa fuera del cuerpo, pero aún en el encabezado, debe marcar el método como ''en línea'' explícitamente.
namespace test_ns{
class TestClass{
public:
inline void testMethod();
};
void TestClass::testMethod(){
// some code here...
}
} // end namespace test_ns
Editar
Para mí, a menudo ayuda a resolver este tipo de problemas de compilación al darme cuenta de que el compilador no ve nada como un archivo de encabezado. Los archivos de encabezado se preprocesan y el compilador solo ve un archivo enorme que contiene cada línea de cada archivo (recursivamente) incluido. Normalmente, el punto de partida para estos recursivos incluye es un archivo fuente cpp que se está compilando. En nuestra compañía, incluso un archivo cpp de apariencia modesta se puede presentar al compilador como un monstruo de 300000 líneas.
Entonces, cuando un método, que no está declarado en línea, se implementa en un archivo de cabecera, el compilador podría terminar viendo vacío TestClass :: testMethod () {...} docenas de veces en el archivo preprocesado. Ahora puede ver que esto no tiene sentido, el mismo efecto que obtendría al copiarlo / pegarlo varias veces en un archivo fuente. E incluso si tuvo éxito solo al tenerlo una vez en cada unidad de compilación, mediante alguna forma de compilación condicional (por ejemplo, usando corchetes de inclusión), el enlazador aún encontraría el símbolo de este método en múltiples unidades compiladas (archivos de objeto).
En realidad, es posible tener definiciones en un único archivo de encabezado (sin un archivo .c / .cpp separado) y aún así poder usarlo desde múltiples archivos fuente.
Considere este encabezado de foobar.h
:
#ifndef FOOBAR_H
#define FOOBAR_H
/* write declarations normally */
void foo();
void bar();
/* use conditional compilation to disable definitions when necessary */
#ifndef ONLY_DECLARATIONS
void foo() {
/* your code goes here */
}
void bar() {
/* your code goes here */
}
#endif /* ONLY_DECLARATIONS */
#endif /* FOOBAR_H */
Si usa este encabezado en un solo archivo fuente, inclúyalo y úselo normalmente. Como en main.c
:
#include "foobar.h"
int main(int argc, char *argv[]) {
foo();
}
Si hay otros archivos fuente en su proyecto que requieren foobar.h
, entonces #define ONLY_DECLARATIONS
macro #define ONLY_DECLARATIONS
antes de incluirla. En use_bar.c
puedes escribir:
#define ONLY_DECLARATIONS
#include "foobar.h"
void use_bar() {
bar();
}
Después de la compilación, use_bar.o y main.o se pueden vincular entre sí sin errores, porque solo uno de ellos (main.o) tendrá implementación de foo () y bar ().
Eso es poco idiomático, pero permite mantener definiciones y declaraciones juntas en un archivo. Siento que es un sustituto de los pobres para los módulos verdaderos.