2010-01-20 21 views
9

que tienen una matriz de caracteres definido en una cabeceraguardias de cabecera y LNK4006

//header.h 
const char* temp[] = {"JeffSter"}; 

La cabecera si #defined vigilado y tiene un pragma una vez en la parte superior. Si este encabezado está incluido en varios lugares, obtengo un LNK4006 - char const * * temp ya definido en blahblah.obj. Entonces, tengo un par de preguntas sobre esto

  1. ¿Por qué sucede esto si tengo las protecciones en su lugar? Pensé que impedían que se leyera el encabezado después del primer acceso.
  2. ¿Por qué las numerosas enumeraciones en este encabezado no dan también las advertencias de LNK4006?
  3. Si agrego estática antes de la firma, no aparece la advertencia. ¿Cuáles son las implicaciones de hacerlo de esta manera?
  4. Existe una forma mejor de hacer esto que evita el error, pero me permite declarar la matriz en el encabezado. Realmente odiaría tener un archivo cpp solo para una definición de matriz.

Respuesta

12

¿Por qué ocurre esto si tengo los protectores en su lugar? Pensé que impedían que se leyera el encabezado después del primer acceso.

Incluir guardias aseguran que una cabecera se incluye sólo una vez en un archivo (unidad de traducción). Para varios archivos, incluido el encabezado, desea que el encabezado se incluya en cada archivo.

Por definir, en contraposición a declarar variables con vinculación externa (variables globales) en el archivo de cabecera, sólo se puede incluir en la cabecera del archivo de origen de una vez. Si incluye el encabezado en múltiples archivos fuente, habrá múltiples definiciones de una variable, lo cual no está permitido en C++.

Por lo tanto, como ha descubierto, no es una buena idea definir variables en un archivo de encabezado precisamente por el motivo anterior.

¿Por qué las numerosas enumeraciones en este encabezado no dan también las advertencias LNK4006?

Porque no definen "variables globales", son solo declaraciones sobre tipos, etc. No reservan ningún almacenamiento.

Si agrego estática antes de la firma, no aparece la advertencia. ¿Cuáles son las implicaciones de hacerlo de esta manera?

Cuando realiza una variable static, que tiene alcance estática. El objeto no es visible fuera de la unidad de traducción (archivo) en la que está definido. Por lo tanto, en términos simples, si usted tiene:

static int i; 

en su encabezado, cada archivo de origen en el que se incluirá la cabecera tendrá un separadaint variable de i, que está fuera invisible del archivo de origen. Esto se conoce como enlace interno.

Hay una manera mejor de hacer esto que evita el error, pero me permite declarar la matriz en el encabezado. Realmente odiaría tener un archivo cpp solo para una definición de matriz.

Si desea que la matriz sea un objeto visible desde todos los archivos de C++, que debe hacer:

extern int array[SIZE]; 

en el archivo de cabecera, y luego incluir el archivo de cabecera en todos los archivos de código fuente en C++ que necesitan la variable array. En uno de los (.cpp) archivos de origen, es necesario definir array:

int array[SIZE]; 

debe incluir la cabecera en el archivo fuente anterior, así, para permitir la captura de errores debido a una diferencia en la cabecera y el archivo fuente.

Básicamente, extern le dice al compilador que "array se define en alguna parte, y tiene el tipo int, y el tamaño SIZE". Entonces, realmente definearray una sola vez. En la etapa de enlace, todo se resuelve muy bien.

4

Incluya las protecciones que lo protegen de incluir el mismo encabezado en el mismo archivo repetidamente, pero no de incluirlo en distintos archivos.
Lo que sucede es que el enlazador ve temp en más de un archivo de objetos - se puede resolver que al hacer temp estática o ponerlo en un espacio de nombres sin nombre:

static const char* temp1[] = {"JeffSter"}; 
// or 
namespace { 
    const char* temp2[] = {"JeffSter"}; 
} 

Alternativamente, puede utilizar un archivo de fuente que define temp y acaba de declarar como extern en la cabecera:

// temp.cpp: 
const char* temp[] = {"JeffSter"}; 

// header.h: 
extern const char* temp[]; 
-1

Espera ... usted está mezclando hasta sus declaraciones ... que dijiste 'const char * * temp' todavía en el archivo de cabecera que tiene 'const char * temp [] = {"JeffSter"}; '.

Véase la sección 6.1 del C FAQ, bajo 'Sección 6. Arrays y punteros', por citar:

 
6.1: I had the definition char a[6] in one source file, and in 
    another I declared extern char *a. Why didn't it work? 

A: In one source file you defined an array of characters and in the 
    other you declared a pointer to characters. The declaration 
    extern char *a simply does not match the actual definition. 
    The type pointer-to-type-T is not the same as array-of-type-T. 
    Use extern char a[]. 

    References: ISO Sec. 6.5.4.2; CT&P Sec. 3.3 pp. 33-4, Sec. 4.5 
    pp. 64-5. 

Ese es el origen del problema. Haga coincidir su declaración y definiciones. Lo siento si esto suena contundente, pero no pude evitar notar lo que el enlazador le estaba diciendo ...

Espero que esto ayude, Saludos cordiales, Tom.

+1

el enlazador solo está traduciendo la sintaxis de la matriz a la sintaxis del puntero. No es un error, es solo una advertencia, el programa se ejecuta como se esperaba. – Steve

4
  1. guardias de cabecera no tienen absolutamente nada que ver con la prevención de múltiples definiciones en su totalidad programa. El objetivo de las protecciones de encabezado es evitar la inclusión múltiple del mismo archivo de encabezado en la misma unidad de traducción (archivo .cpp). En otras palabras, existen para evitar definiciones múltiples en el mismo archivo fuente. Y funcionan según lo previsto en su caso.

  2. La regla que rige los problemas de definición múltiple en C++ se denomina Regla de definición única (ODR). ODR se define de manera diferente para diferentes tipos de entidades. Por ejemplo, tipos pueden tener múltiples definiciones idénticas en el programa. Pueden (y la mayoría de las veces tener a) definirse en cada unidad de traducción donde se usan.Esta es la razón por la que su definición enumerativa no genera un error.

    Los objetos con enlace externo son una historia completamente diferente. Deben definirse en una y solo una unidad de traducción. Esta es la razón por la que su definición de temp causa un error cuando se incluye el archivo de encabezado en varias unidades de traducción. Incluir guardias no puede evitar este error. Simplemente no defina objetos con enlaces externos en archivos de encabezado.

  3. Al agregar static le da a su objeto enlace interno. Esto hará que el error desaparezca, ya que ahora está perfectamente bien desde el punto de vista ODR. Pero esto definirá un objeto temp independiente en cada unidad de traducción en la que se incluye el archivo de encabezado. Para lograr el mismo efecto también se puede hacer

    const char* const temp[] = { "JeffSter" }; 
    

    desde const objetos en C++ tienen vinculación interna por defecto.

  4. Esto depende de si necesita un objeto con enlace externo (es decir, uno para todo el programa) o un objeto con enlace interno (exclusivo para cada unidad de traducción). Si necesita este último, use static y/o const adicional (si le resulta útil) como se muestra arriba.

    Si necesita el anterior (enlace externo), se debería poner una declaración no definir en el archivo de cabecera

    extern const char* temp[]; 
    

    y mover la definición en uno y sólo un archivo .cpp

    char* const temp[] = { "JeffSter" }; 
    

    La declaración anterior en el archivo de encabezado funcionará para la mayoría de los propósitos. Sin embargo, declara temp como una matriz de tamaño desconocido, un tipo incompleto. Si desea declarar como una matriz de tamaño conocido, tiene que especificar el tamaño manualmente

    extern const char* temp[1]; 
    

    y recuerde mantenerlo en sincronía entre la declaración y definición.

0

Respetuosamente discrepo con el consejo contra la definición de variables en los encabezados, porque creo que "nunca" es demasiado amplio. Sin embargo, el episodio que me llevó a este hilo ofrece una historia de advertencia para aquellos que se atreven a hacerlo.

Aterricé en esta página como resultado de una consulta sobre la causa del aviso LNK4006, llamando a una matriz largamente establecida que acabo de mover de la unidad de traducción que define mi rutina DLLMain en el encabezado privado que se incluye en la mayoría de las unidades de traducción que componen esta biblioteca. He compilado esta biblioteca cientos de veces en los últimos 11 años, y nunca antes había visto esta advertencia.

Poco después de leer esta página, descubrí la causa del error, que era que la definición estaba fuera del bloque de guardia que protege todo lo demás que está definido en el módulo que también define DLLMain, que es donde suelo reunir todos los bloques de memoria que necesitan conexión externa. Como era de esperar, mover la mesa dentro del bloque de guardia eliminó las advertencias, dejándome solo con dos, relacionadas con una nueva tabla vinculada externamente, para resolver.

Takeaway: Puede definir variables en los encabezados, y es un gran lugar para poner bloques comunes, pero tenga en cuenta sus guardias.