Archivos de encabezado (C++)
Los nombres de elementos de programa, como variables, funciones, clases, etc., deben declararse antes de poder usarse. Por ejemplo, no puede solo escribir x = 42
sin declarar primero "x".
int x; // declaration
x = 42; // use x
La declaración indica al compilador si el elemento es un int
, un double
, una función, un class
o alguna otra cosa. Además, cada nombre debe declararse (directa o indirectamente) en cada archivo .cpp en el que se use. Al compilar un programa, cada archivo .cpp se compila de forma independiente en una unidad de compilación. El compilador no tiene conocimiento de los nombres que se declaran en otras unidades de compilación. Esto significa que si define una clase o función o variable global, debe proporcionar una declaración de ese elemento en cada archivo .cpp adicional que lo use. Cada declaración de ese elemento debe ser exactamente idéntica en todos los archivos. Una ligera incoherencia provocará errores, o un comportamiento no deseado, cuando el enlazador intente combinar todas las unidades de compilación en un único programa.
Para minimizar el potencial de errores, C++ ha adoptado la convención de usar archivos de encabezado para contener declaraciones. Usted realiza las declaraciones en un archivo de encabezado y, a continuación, se usa la directiva #include en cada archivo .cpp u otro archivo de encabezado que requiera esa declaración. La directiva #include inserta una copia del archivo de encabezado directamente en el archivo .cpp antes de la compilación.
Nota:
En Visual Studio 2019, la característica de módulos de C++20 se presenta como una mejora y reemplazo posible de los archivos de encabezado. Para obtener más información, consulte Información general de los módulos en C++.
Ejemplo
El ejemplo siguiente muestra una manera común de declarar una clase y, a continuación, usarla en un archivo de código fuente diferente. Comenzaremos con el archivo de encabezado, my_class.h
. Contiene una definición de clase, pero tenga en cuenta que la definición está incompleta; la función miembro do_something
no está definida:
// my_class.h
namespace N
{
class my_class
{
public:
void do_something();
};
}
A continuación, cree un archivo de implementación (normalmente con una extensión .cpp o similar). Llamaremos al archivo my_class.cpp y proporcionaremos una definición para la declaración de miembro. Agregamos una directiva #include
para el archivo "my_class.h" para que la declaración de my_class se inserte en este punto en el archivo .cpp e incluiremos <iostream>
para extraer la declaración de std::cout
. Tenga en cuenta que las comillas se usan para los archivos de encabezado en el mismo directorio que el archivo de origen y los corchetes angulares se usan para encabezados de biblioteca estándar. Además, muchos encabezados de biblioteca estándar no tienen .h ni ninguna otra extensión de archivo.
En el archivo de implementación, opcionalmente podemos usar una instrucción using
para evitar tener que calificar todas las menciones de "my_class" o "cout" con "N::" o "std::". No coloque instrucciones using
en los archivos de encabezado.
// my_class.cpp
#include "my_class.h" // header in local directory
#include <iostream> // header in standard library
using namespace N;
using namespace std;
void my_class::do_something()
{
cout << "Doing something!" << endl;
}
Ahora podemos usar my_class
en otro archivo .cpp. Usamos #include en el archivo de encabezado para que el compilador extraiga la declaración. Todo el compilador debe saber que my_class es una clase que tiene una función miembro pública denominada do_something()
.
// my_program.cpp
#include "my_class.h"
using namespace N;
int main()
{
my_class mc;
mc.do_something();
return 0;
}
Después de que el compilador termine de compilar cada archivo .cpp en archivos .obj, pasa los archivos .obj al enlazador. Cuando el enlazador combina los archivos de objeto, encuentra exactamente una definición para my_class; está en el archivo .obj generado para my_class.cpp y la compilación se realiza correctamente.
Incluir restricciones
Normalmente, los archivos de encabezado tienen una directiva incluir restricciones o #pragma once
para asegurarse de que no se insertan varias veces en un único archivo .cpp.
// my_class.h
#ifndef MY_CLASS_H // include guard
#define MY_CLASS_H
namespace N
{
class my_class
{
public:
void do_something();
};
}
#endif /* MY_CLASS_H */
Qué colocar en un archivo de encabezado
Dado que un archivo de encabezado podría incluirse en varios archivos, no puede contener definiciones que puedan generar varias definiciones del mismo nombre. No se permite lo siguiente, o se considera una práctica muy desaconsejable:
- definiciones de tipo integradas en el espacio de nombres o el ámbito global
- definiciones de funciones no insertadas
- definiciones de variables no constantes
- definiciones de agregado
- espacios de nombres sin nombre
- Directivas using
El uso de la directiva using
no provocará necesariamente un error, pero puede causar un problema porque lleva el espacio de nombres al ámbito en cada archivo .cpp que incluya directa o indirectamente ese encabezado.
Archivo de encabezado de ejemplo
En el ejemplo siguiente se muestran los distintos tipos de declaraciones y definiciones que se permiten en un archivo de encabezado:
// sample.h
#pragma once
#include <vector> // #include directive
#include <string>
namespace N // namespace declaration
{
inline namespace P
{
//...
}
enum class colors : short { red, blue, purple, azure };
const double PI = 3.14; // const and constexpr definitions
constexpr int MeaningOfLife{ 42 };
constexpr int get_meaning()
{
static_assert(MeaningOfLife == 42, "unexpected!"); // static_assert
return MeaningOfLife;
}
using vstr = std::vector<int>; // type alias
extern double d; // extern variable
#define LOG // macro definition
#ifdef LOG // conditional compilation directive
void print_to_log();
#endif
class my_class // regular class definition,
{ // but no non-inline function definitions
friend class other_class;
public:
void do_something(); // definition in my_class.cpp
inline void put_value(int i) { vals.push_back(i); } // inline OK
private:
vstr vals;
int i;
};
struct RGB
{
short r{ 0 }; // member initialization
short g{ 0 };
short b{ 0 };
};
template <typename T> // template definition
class value_store
{
public:
value_store<T>() = default;
void write_value(T val)
{
//... function definition OK in template
}
private:
std::vector<T> vals;
};
template <typename T> // template declaration
class value_widget;
}
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de