Cuándo usar extern en C ++

399

Estoy leyendo "Think in C ++" y acaba de presentar la externdeclaración. Por ejemplo:

extern int x;
extern float y;

Creo que entiendo el significado (declaración sin definición), pero me pregunto cuándo será útil.

¿Alguien puede dar un ejemplo?

Aslan986
fuente
1
He tenido que proporcionar una definición externen varias ocasiones. Las herramientas de Microsoft produjeron un error de enlace para los símbolos faltantes cuando solo se definieron las tablas en otro archivo fuente. El problema era que la tabla era consty el compilador de C ++ lo promovió staticen la unidad de traducción. Ver, por ejemplo, ariatab.cppy kalynatab.cpp.
jww
2
Y creo que la respuesta de Nik es la correcta porque es el único que parece haber respondido una pregunta de C ++. Todos los demás parecen haberse desviado de una pregunta C.
jww

Respuestas:

520

Esto resulta útil cuando tiene variables globales. Usted declara la existencia de variables globales en un encabezado, de modo que cada archivo fuente que incluye el encabezado lo sepa, pero solo necesita "definirlo" una vez en uno de sus archivos fuente.

Para aclarar, usar extern int x;le dice al compilador que un objeto de tipo intllamado xexiste en alguna parte . No es el trabajo de los compiladores saber dónde existe, solo necesita saber el tipo y el nombre para saber cómo usarlo. Una vez que se hayan compilado todos los archivos de origen, el vinculador resolverá todas las referencias xa la definición que encuentra en uno de los archivos de origen compilados. Para que funcione, la definición de la xvariable debe tener lo que se llama "enlace externo", lo que básicamente significa que debe declararse fuera de una función (en lo que generalmente se llama "el alcance del archivo") y sin la staticpalabra clave.

encabezamiento:

#ifndef HEADER_H
#define HEADER_H

// any source file that includes this will be able to use "global_x"
extern int global_x;

void print_global_x();

#endif

fuente 1:

#include "header.h"

// since global_x still needs to be defined somewhere,
// we define it (for example) in this source file
int global_x;

int main()
{
    //set global_x here:
    global_x = 5;

    print_global_x();
}

fuente 2:

#include <iostream>
#include "header.h"

void print_global_x()
{
    //print global_x here:
    std::cout << global_x << std::endl;
}
dreamlax
fuente
15
Gracias. Entonces, si declaro una variable global en un archivo de encabezado sin la palabra clave externa, ¿los archivos de origen que incluyen el encabezado no lo ven?
Aslan986
23
usted no debe declarar VARs globales en un encabezado, porque entonces cuando 2 archivos incluyen el mismo archivo de cabecera, no va a enlace (enlazador emitirá un error de "símbolo duplicado")
Kuba
63
@ Aslan986: No, algo peor sucede. Cada archivo fuente que incluye el encabezado tendrá su propia variable, por lo que cada archivo fuente se compilará de forma independiente, pero el vinculador se quejará porque dos archivos fuente tendrán los mismos identificadores globales.
dreamlax
77
Cuando no usa la palabra "extern", entonces la variable existe. Cuando usas "extern", es un "oye, hay esta var en otro lugar". Perdón por no responder si se trata de una definición o declaración, ya que siempre me confundo con estos dos.
kuba
3
@CCJ: el protector de inclusión solo funciona para el archivo fuente que lo incluye. Detiene que el mismo encabezado se incluya dos veces dentro del mismo archivo de origen (en caso de que otros encabezados también lo incluyan, etc.). Entonces, incluso con los guardias de inclusión, cada archivo de origen que incluye el encabezado tendrá su propia definición.
dreamlax
172

Es útil cuando comparte una variable entre unos pocos módulos. Lo define en un módulo y usa extern en los otros.

Por ejemplo:

en file1.cpp:

int global_int = 1;

en file2.cpp:

extern int global_int;
//in some function
cout << "global_int = " << global_int;
MByD
fuente
39
Esta respuesta es más correcta que la aceptada, ya que no utiliza el archivo de encabezado y establece claramente que es útil solo cuando se comparte entre pocos módulos. Para aplicaciones más grandes es mejor usar, por ejemplo, una clase ConfigManager.
Zac
1
¿Hay algún problema cuando los espacios de nombres están involucrados, global_intestá en el espacio de nombres global, si tuviera que usarlo en file2.cpp en alguna sección de espacio de nombres tendría que determinarlo correctamente? es decirnamespace XYZ{ void foo(){ ::global_int++ } };
jxramos
8
@Zac: Por otro lado, al no declarar una variable global en un encabezado, sin darse cuenta ha hecho que sea mucho más difícil determinar dónde se define realmente. Por lo general, si ve una variable global declarada en abc.h, hay una buena probabilidad de que se defina en abc.cpp. Un buen IDE siempre ayudará, pero un código bien organizado siempre es una mejor solución.
dreamlax
sin externen file2.cpp, aún puede acceder a global_intafter include. ¿Por qué necesito tenerlo?
TomSawyer
62

Se trata de la vinculación .

Las respuestas anteriores proporcionaron buenas explicaciones sobre extern.

Pero quiero agregar un punto importante.

Pregunta externen C ++ no en C y no sé por qué no hay una respuesta que mencione sobre el caso cuando externviene con constC ++.

En C ++, una constvariable tiene un enlace interno por defecto (no como C).

Entonces este escenario conducirá a un error de enlace :

Fuente 1:

const int global = 255; //wrong way to make a definition of global const variable in C++

Fuente 2:

extern const int global; //declaration

Tiene que ser así:

Fuente 1:

extern const int global = 255; //a definition of global const variable in C++

Fuente 2:

extern const int global; //declaration
Trevor
fuente
2
¿Por qué es un error mientras funciona en c ++ sin incluir 'extern' en la parte de definición?
Jefe Shifter
1
Parece que no encuentro ese error de vinculación en VIsual Studio con Visual Micro. ¿Qué me estoy perdiendo?
Craig.Feied
1
@ lartist93 @ Craig.Feied Creo que es posible que deba volver a comprobarlo cuidadosamente. Incluso en el caso de que el compilador no informe un error de enlace, ¿podría verificar que ambos objetos en ambos orígenes sean iguales sin externdefinición? Puede hacerlo imprimiendo el valor de globalen la fuente 2.
Trevor
3
Confirmar, en MSVS 2018 no es un error de vinculación si externse omite en const int global = 255;.
Evg
13

Esto es útil cuando desea tener una variable global. Usted define las variables globales en algún archivo fuente y las declara externas en un archivo de encabezado para que cualquier archivo que incluya ese archivo de encabezado vea la misma variable global.

Marlon
fuente
De todos modos, esto no suena muy POO, los pondría en una clase singleton ... o una función que devuelve un valor estático local ...
RzR