2010-02-24 22 views
47

si defino mis constantes varibles en mi cabecera como esta ...las variables constantes que no trabajan en la cabecera

extern const double PI = 3.1415926535; 
extern const double PI_under_180 = 180.0f/PI; 
extern const double PI_over_180 = PI/180.0f; 

me sale el siguiente error

1>MyDirectX.obj : error LNK2005: "double const PI" ([email protected]@3NB) already defined in main.obj 
1>MyDirectX.obj : error LNK2005: "double const PI_under_180" ([email protected]@3NB) already defined in main.obj 
1>MyDirectX.obj : error LNK2005: "double const PI_over_180" ([email protected]@3NB) already defined in main.obj 
1>MyGame.obj : error LNK2005: "double const PI" ([email protected]@3NB) already defined in main.obj 
1>MyGame.obj : error LNK2005: "double const PI_under_180" ([email protected]@3NB) already defined in main.obj 
1>MyGame.obj : error LNK2005: "double const PI_over_180" ([email protected]@3NB) already defined in main.obj 

pero Si quito esas constantes de la cabecera y ponerlos en el documento que está incluyendo la cabecera como esta ...

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f/PI; 
const double PI_over_180 = PI/180.0f; 

funciona

¿Alguien tiene idea de lo que podría estar haciendo mal?

Gracias

+0

Probablemente deberías escribir '180.0' en lugar de' 180.0f' ya que estás tratando con dobles en lugar de flotadores. También cambie el nombre 'PI' a algo más único. 'PI' se usa en muchas bibliotecas como una macro y si lo usa, puede obtener resultados extraños. – thebretness

+0

Pierde el 'extern' y estarás bien. – sellibitze

+1

@sellibitze: No en C, donde los objetos 'const' tienen un enlace externo por defecto, lo que significa que' extern' no cambia nada. – AnT

Respuesta

114

El problema es que define objetos con enlace externo en el archivo de encabezado. Es de esperar que una vez que incluya ese archivo de encabezado en varias unidades de traducción, obtendrá múltiples definiciones del mismo objeto con enlace externo, que es un error.

La forma correcta de hacerlo depende de su intención.

(1) Usted puede poner sus definiciones en el archivo de cabecera, pero asegúrese de que tienen enlace interno.

En C que requeriría una explícita static

static const double PI = 3.1415926535; 
static const double PI_under_180 = 180.0f/PI; 
static const double PI_over_180 = PI/180.0f; 

En C++ static es opcional (ya que en C++ const objetos tienen vinculación interna por defecto)

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f/PI; 
const double PI_over_180 = PI/180.0f; 

(2) O puede poner simples declaraciones no definidas en el archivo de encabezado y poner las definiciones en una (y sólo una) archivo de implementación

Las declaraciones en el archivo cabecera deben incluir una explícita extern y sin inicializador

extern const double PI; 
extern const double PI_under_180; 
extern const double PI_over_180; 

y las definiciones en una aplicación archivo debería ser tan siguiente

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f/PI; 
const double PI_over_180 = PI/180.0f; 

(explícita en extern las definiciones son opcionales, si las declaraciones anteriores preceden a las definiciones en la misma unidad de traducción).

El método que elija dependerá de su intención.

El primer método facilita al compilador la optimización del código, ya que puede ver el valor real de la constante en cada unidad de traducción. Pero al mismo tiempo, conceptualmente obtienes objetos constantes separados e independientes en cada unidad de traducción. Por ejemplo, &PI evaluará a una dirección diferente en cada unidad de traducción.

El segundo método crea realmente global constantes, es decir, objetos únicos constantes que son compartidos por todo el programa. Por ejemplo, &PI evaluará a la misma dirección en cada unidad de traducción. Pero en este caso, el compilador solo puede ver los valores reales en una y solo una unidad de traducción, lo que puede impedir las optimizaciones.

+1

+1. Solo quería mencionar que en C una variable 'static const' no es una expresión constante como la que necesitaría para el tamaño de una matriz, por ejemplo. Supongo que es por eso que '# define's son más populares en C. – sellibitze

+3

+1 para una buena descripción y enseñarme algo que no sabía: "en C++ los objetos constantes tienen un enlace interno por defecto" –

+0

@sellibitze: Sí, pero esto se convierte principalmente en un problema con las constantes integrales. El impacto en objetos constantes no integrales es insignificante, si es que existe. – AnT

5

La clase extern de almacenamiento para ellos es casi seguro que la causa del problema que se está viendo. Si lo elimina, el código probablemente estará bien (al menos a este respecto).

Editar: Me acabo de dar cuenta de que ha etiquetado esto como C y C++. En este sentido, C y C++ son realmente bastante diferentes (pero a partir de los mensajes de error, aparentemente está compilando como C++, no como C). En C++, desea eliminar el extern, porque (de forma predeterminada) las variables const tienen la clase de almacenamiento static. Eso significa que cada archivo fuente (unidad de traducción) obtendrá su propia "copia" de la variable, y no habrá ningún conflicto entre las definiciones en diferentes archivos. Dado que usted (probablemente) solo usa los valores, no los trata como variables, tener múltiples "copias" no dañará nada, a ninguno de ellos se le asignará espacio de almacenamiento.

En C, extern es bastante diferente, y la eliminación de extern no hará ninguna diferencia real, ya que serán extern de forma predeterminada. En este caso, realmente necesita inicializar las variables en exactamente un lugar, y declararlas extern en el encabezado. Alternativamente, puede agregar la clase de almacenamiento static que C++ agregará de forma predeterminada cuando/si elimina el extern del encabezado.

1

Debe declarar los contenidos en el encabezado y luego definirlos en uno de sus archivos de código. Si no los declara en ningún lado, existe un error del enlazador cuando intenta vincular la declaración a la definición real. También puede salirse con la suya usando instrucciones #ifdef para tener una definición dentro del encabezado.

Asegúrese de que estén declarados en un encabezado que sea incluido por todos los que lo necesiten y asegúrese de que estén definidos exactamente una vez.

Jacob

8

extern significa la 'verdadera' definición de la variable está en otra parte, y el compilador debe confiar en que las cosas van a conectar en tiempo de enlace. Tener la definición en línea con el extern es extraño y es lo que está afectando a su programa. Si desea que sean extern, simplemente defínalos exactamente una vez en otro lugar de su programa.

1

Si desea definir constantes en archivos de encabezado, use static const. Si usa extern, el vinculador es correcto para quejarse sobre definiciones múltiples porque cada archivo fuente incluido proporcionará memoria para la variable si asigna un valor.

2

Parece que el archivo de encabezado se está incluyendo varias veces. Debes agregar guardias.

En la parte superior de cada archivo de cabecera que debe tener algo como:

#ifndef MY_HEADER_FILE_NAME_H 
#define MY_HEADER_FILE_NAME_H 

... 

// at end of file 
#endif 

Si está utilizando g ++ o MSVC entonces usted puede simplemente añadir:

#pragma once 

En la parte superior de cada cabecera archivo, pero eso no es 100% portátil.

Además, no se debe definir constantes en los archivos de cabecera, sólo se lo cuenten:

// In header file 
extern const int my_const; 


// In one source file 
const int my_const = 123; 
+3

Incluir guardias solo protege contra inclusión múltiple en el mismo archivo. La falta de ellos producirá un problema en el momento de la compilación. Se encuentra con un problema en el momento del enlace, debido a intentar definir los mismos símbolos una vez cada uno en varios archivos. –

+0

Me dirigí a eso en mi respuesta. –

0

en declarar const global dentro de cabecera provoca que cada unidad de compilación que incluye este hader tendrá propias definiciones definiciones globales con el mismo nombre. Entonces al enlazador no le gusta eso.

Si realmente necesita estos en el encabezado, entonces probablemente debe declararlos como estáticos.

2

El problema es que está inicializando las variables en el archivo de encabezado; esto crea una declaración definiendo, que se repite en cada archivo que incluye ese encabezado, de ahí el error de definición múltiple.

¿Quieres una declaraciónno -Definición (sin inicializador) en el archivo de cabecera, y poner la declaración de definición en uno de los archivos de implementación.

2

Muchas respuestas incorrectas a continuación. Los que son correctos son los que te dicen que elimines el extern, ya que sellibitze también dijo en su comentario que son correctos.

Como se declaran const, no hay ningún problema para tener la definición en el encabezado. C++ alineará un const para un tipo incorporado a menos que intente tomar su dirección (un puntero a const) en cuyo caso lo instanciará con el enlace static, también puede obtener múltiples instancias en módulos separados, pero a menos que espere todos los punteros al mismo const para tener la misma dirección, esto no es un problema.

+0

MSVC tienen un comportamiento diferente al esperado. Puede obtener símbolos duplicados si declara una variable como 'const' en un encabezado. Necesita usar ambos 'const estáticos' para garantizar la vinculación interna. Tal vez es un error del compilador. – jww

+0

@jww: en la pregunta se informó que funcionó sin estática. El comportamiento de C _does_ difiere, pero dado que el código en cuestión está vinculado, debe haber utilizado la compilación de C++. Sin embargo, la pregunta está etiquetada tanto en C como en C++, por lo que discutiblemente debería haber cubierto ambas, pero la pregunta no era sobre las diferencias entre C y C++, así que supongo que me etiquetaron mal. La pregunta tiene 7 años de antigüedad, por lo que no garantiza la actualización. – Clifford

0

Una vieja pregunta, de hecho, pero falta una respuesta útil.

Es posible engañar MSVC a aceptar constantes estáticas en cabeceras simplemente envolviendo los de una plantilla de clase "ficticia":

template <typename Dummy = int> 
struct C { 
    static const double Pi; 
}; 

template <typename Dummy = int> 
const double C<Dummy>::Pi = 3.14159; 

Ahora, C <> :: PI se puede acceder desde otro lugar. Sin redefinición se queja; La constante es directamente accesible en cada unidad de compilación sin una elegante optimización de tiempo de enlace. La macro se puede desplegar para embellecer aún más este enfoque (aunque las macros son malvadas).

+0

¿Hay algún inconveniente en el uso de este método? – PolyMesh

+0

Un poco de desventaja sintáctica, como se mencionó, uno tendrá que referirse a Pi como C <> :: Pi a lo largo del código. Pero entonces, las constantes globales sueltas se evitan mejor de todos modos (por lo que MathConst <> :: Pi es marginalmente peor que MathConst :: Pi). – oakad

Cuestiones relacionadas