21
ARCHIVOS DE CABECERA Se denomina header file, al español fichero cabecera, o include file, al español ficherode inclusión, en ciencias de computación, especialmente en el ámbito de los lenguajesde programación C y C++, al archivo, normalmente en forma de código fuente, que elcompilador incluye de forma automática al procesar algún otro archivo fuente.Típicamente los programadores especifican la inclusión de los header files por mediode pragmas al comienzo (head o cabecera) de otro archivo fuente.Un header file contiene, normalmente, una declaración directa de clases, subrutinas,variables, u otros identificadores. Aquellos programadores que desean declararidentificadores estándares en más de un archivo fuente pueden colocar esosidentificadores en un único header file, que se incluirá cuando el código que contienesea requerido por otros archivos.Si se adopta el modelo modular entonces se querrá tener para cada módulo lasdefiniciones de las variables, los prototipos de las funciones, etc. Sin embargo, ¿quésucede si varios módulos necesitan compartir tales definiciones? En tal caso, lo mejores centralizar las definiciones en un archivo, y compartir el archivo entre los módulos.Tal archivo es usualmente llamado un archivo cabecera .Por convención estos archivos tienen el sufijo .h Se han revisado ya algunos archivos cabecera de la biblioteca estándar, por ejemplo: #include <stdio.h>

Ficheros y Cabeceras

Embed Size (px)

Citation preview

Page 1: Ficheros y Cabeceras

ARCHIVOS DE CABECERA

Se denomina header file, al español fichero cabecera, o include file, al español ficherode

inclusión, en ciencias de computación, especialmente en el ámbito de los lenguajesde

programación C y C++, al archivo, normalmente en forma de código fuente, que

elcompilador incluye de forma automática al procesar algún otro archivo

fuente.Típicamente los programadores especifican la inclusión de los header files por

mediode pragmas al comienzo (head o cabecera) de otro archivo fuente.Un header file

contiene, normalmente, una declaración directa de clases, subrutinas,variables, u otros

identificadores. Aquellos programadores que desean declararidentificadores estándares

en más de un archivo fuente pueden colocar esosidentificadores en un único header file,

que se incluirá cuando el código que contienesea requerido por otros archivos.Si se adopta

el modelo modular entonces se querrá tener para cada módulo lasdefiniciones de las

variables, los prototipos de las funciones, etc. Sin embargo, ¿quésucede si varios módulos

necesitan compartir tales definiciones? En tal caso, lo mejores centralizar las definiciones

en un archivo, y compartir el archivo entre los módulos.Tal archivo es usualmente llamado

un archivo cabecera .Por convención estos archivos tienen el sufijo .h Se han revisado ya

algunos archivos cabecera de la biblioteca estándar, por ejemplo:

#include <stdio.h>

Se pueden definir los propios archivos cabecera y se pueden incluir en el programacomo

se muestra enseguida:

#include "mi_cabecera.h"

Los archivos cabecera por lo general sólo contienen definiciones de tipos de

datos,prototipos de funciones y comandos del preprocesador de C

Page 2: Ficheros y Cabeceras

FUNCIONES DE BIBLIOTECAS

El lenguaje C, C++ contiene numerosas funciones, cuando se emplean funciones deesta

biblioteca estándar, se incrementa la transportabilidad de los programas.C ofrece un

conjunto de funciones estándar que dan soporte a las operaciones que seutilizan con más

frecuencia. Estas funciones están agrupadas en bibliotecas. Parautilizar cualquiera de las

funciones que forman parte de las bibliotecas estándar de C,sólo hace falta realizar una

llamada a dicha función.Los grupos de funciones estándar más comunes son:

Entrada/Salida estándar

Matemáticas

De conversión

Diagnostico

De manipulación de memoria

Control de proceso

Ordenación

Directorios

Fecha y hora

Búsqueda

Manipulación de cadenas

Gráficos

Tipos de ficheros

Los ficheros de cabecera del proyecto se llamarán fichero.h (si trabajamos en C) o

fichero.hh (si trabajamos en C++) (también sería correcto el uso de fichero.hpp para C++)

Los ficheros con el codigo de las funciones no inline se llamará fichero.c (si trabajamos en

C) o fichero.cc (si trabajamos en C++) (también sería correcto el uso de fichero.cpp para C+

+)

Page 3: Ficheros y Cabeceras

Los ficheros con el codigo de las funciones inline se llamará fichero.ic (si trabajamos en C)

o fichero.icc (si trabajamos en C++) (también sería correcto el uso de fichero.ipp)

El separar las funciones inline (funciones definidas en una sóla línea) de las demás

funciones se debe a que algunos debuggers (y más raramente algunos compiladores) no

manejan bien las funciones inline.

Separando las funciones inline en un fichero aparte, podemos conseguir que se incluyan

en los ficheros de cabecera o en los ficheros fuente, según nosotros deseemos de acuerdo

a directivas de compilación.

A continuación se muestra en un ejemplo como se haría...

// At the end of fichero.hh

#if !defined(DEBUG_MODE)

#include <fichero.icc>

#endif

// At the end of fichero.cc

#if defined(DEBUG_MODE)

#include <fichero.icc>

#endif

Con este ejemplo conseguimos que si se compila en modo debug (definiendo

DEBUG_MODE) las funciones inline (funcion.icc) se incluiran en el fichero.hh, mientras que

en el modo normal se incuirian en el fichero.cc

[subir]

Page 4: Ficheros y Cabeceras

10.2 Nombres de los ficheros

Para C en cada fichero de una librería tiene que haber funciones realcionadas con el

mismo fin, y el nombre sería el apropiado para poder identificar el fin de estas funciones

(por ejemplo funcionesFecha.c para recopilar funciones de manejo de fechas).

Para C++ en cada fichero de una librería tiene que haber solamente una clase, y el nombre

del fichero sería el mismo que el de la clase (por ejemplo CMiClase.cc para la

implementación de la clase CMiClase).

[subir]

10.3 Estructura de los ficheros

Los ficheros de cabecera (fichero.h, fichero.hh, fichero.hpp) tendrían que tener una

estructura como la siguiente:

Cabecera del fichero.

Includes si es necesario alguno (se aconseja que en los ficheros de cabecera no vaya

ningún fichero include o el mínimo número imprescindible de ellos).

Primero los includes del sistema (si hace falta alguno)

Después los includes propios del proyecto (si hace falta alguno)

Constantes simbólicas y definiciones de macros que se vayan a utilizar en otros

m&ocaute;dulos que incluyan este.

Definición de tipos que se vayan a utilizar en otros módulos que incluyan este.

Prototipos de las funciones del módulo que puedan ser utilizadas desde otro módulo.

Declaración de las clases (C++) del módulo que puedan ser utilizadas desde otro módulo.

Los ficheros fuente (fichero.c, fichero.cc, fichero.cpp) tendrían que tener una estructura

como la siguiente:

Page 5: Ficheros y Cabeceras

Cabecera del fichero.

Includes necesarios para el módulo.

Primero los includes del sistema.

Después los includes propios del proyecto.

Constantes simbólicas y dfiniciones de macros que solamente vayan a utilizarse en este

módulo.

Definición de tipos que se vayan a utilizar solamente en este módulo.

Variables globales usadas en el módulo.

Primero las declaradas en otros módulos distintos, con la palabra reservada extern.

Después las declaradas en este m&odulo.

Prototipos de las funciones del módulo que sólo se usen en este módulo.

Declaración de las clases (C++) del módulo que sólo se usen en este módulo.

Implementación de las funciones del módulo.

La función principal (main()) tiene que ser la primera, las demás estarán ordenadas por

orden de aparición en la función principal o por orden de llamada, u organizándolas por su

uso o finalidad.

Es aconsejable que las implementaciones estén en el mismo orden que sus prototipos.

Implementación de los métodos de las clases (C++).

Es aconsejable que las implementaciones de los métodos estén en el mismo orden en que

aparecen en las declaraciones de las clases.

Los ficheros que contienen las funciones inline (fichero.ic, fichero.icc, fichero.ipp)

solamente contendrán el código de las funciones inline y tendrán una estructura como la

siguiente:

Cabecera del fichero

Código de las funciones inline.

Se aconseja que estén en el mismo orden en que se declaran en el fichero de cabecera.

[subir]

Page 6: Ficheros y Cabeceras

10.4 Cabeceras de los ficheros

Todos los ficheros tienen que incluir una cabecera que indique los siguientes campos.

Nombre del fichero

Finalidad o uso del módulo (incluyendo los argumentos si en este fichero se encuentra la

función main).

Variables globales afectadas (si las hay).

Nombre del autor y fecha de última modificación.

Historial de moficiaciones, con fecha, motivo y nombre del autor.

[subir]

10.5 Ficheros de cabecera (*.h)

Todos los ficheros de cabecera tienen que tener un mecanismo para impedir que sean

incluidos más de una vez (lo mismo les pasaría a los ficheros de las funciones inline).

Por ejemplo, el siguiente metodo valdría:

#ifndef __FICHERO_H__

#define __FICHERO_H__

// __FICHERO_H__ sería un identificador propio de este fichero (fichero.h)

// ya que lo he construido con el nombre del fichero

...

// Aquí iría todo el contenido del fichero

...

#endif

Page 7: Ficheros y Cabeceras

Todos los ficheros de cabecera incluidos en los ficheros deben serlo exclusivamente por

que se usan.

Hay que evitar la inclusión de ficheros de cabecera que no se usan, por legibilidad, puede

complicar la compilación y el linkado innecesariamente.

Los includes de librerías própias del sistema se realiza indicando el nombre entre

caracteres < y >. Por ejemplo:

#include <stdio.h>

Los includes propios del proyecto se realizarán indicando el nombre entre caracteres ". Por

ejemplo:

#include "CMiClase.hh.h"

Conveciones para los ficheros de cabecera

Cuando se crea una struct que contiene funciones miembro, se está creando un nuevo tipo

de dato. En general, se intenta que ese tipo sea fácilmente accesible. En resumen, se

quiere que la interfaz (la declaración) esté separada de la implmentación (la definición de

los métodos) de modo que la implementación pueda cambiar sin obligar a recompilar el

sistema completo. Eso se consigue poniendo la declaración del nuevo tipo en un fichero de

cabecera.

Cuando yo aprendí a programar en C, el fichero de cabecera era un misterio para mi.

Muchos libros de C no hacen hincapié, y el compilador no obliga a hacer la declaración de

las funciones, así que parecía algo opcional la mayor parte de las veces, excepto cuando se

declaraban estrucutras. En C++ el uso de los ficheros de cabecera se vuelve claro como el

cristal. Son prácticamente obligatorios para el desarrollo de programas sencillos, y en ellos

podrá información muy específica: declaraciones. El fichero de cabecera informa al

compilador de lo que hay disponible en la librería. Puede usar la librería incluso si sólo se

Page 8: Ficheros y Cabeceras

dispone del fichero de cabecera y el fichero objeto o el fichero de librería; no necesita

disponer del código fuente del fichero cpp. En el fichero de cabecera es donde se guarda la

especificación de la interfaz.

Aunque el compilador no lo obliga, el mejor modo de construir grandes proyectos en C es

usar librerías; colecciones de funciones asociadas en un mismo módulo objeto o librería, y

usar un fichero de cabecera para colocar todas las declaraciones de las funciones. Es de

rigor en C++, Podría meter cualquier función en una librería C, pero el tipo abstracto de

dato C++ determina las funciones que están asociadas por medio del acceso común a los

datos de una struct. Cualquier función miembro debe ser declarada en la declaración de la

struct; no puede ponerse en otro lugar. El uso de librerías de funciones fue fomentado en

C y institucionalizado en C++.

4.7.1. Importancia de los ficheros de cabecera

Cuando se usa función de una librería, C le permite la posibilidad de ignorar el fichero de

cabecera y simplemente declarar la función a mano. En el pasado, la gente hacía eso a

veces para acelerar un poquito la compilación evitando la tarea de abrir e incluir el fichero

(eso no supone ventaja alguna con los compiladores modernos). Por ejemplo, la siguiente

es una declaración extremadamente vaga de la función printf() (de <stdio.h>):

printf(...);

Estos puntos suspensivos [49] especifican una lista de argumentos variable [50], que dice:

la printf() tiene algunos argumentos, cada uno con su tipo, pero no se sabe cuales.

Simplemente, coge los argumentos que veas y aceptalos. Usando este tipo de declaración,

se suspenden todas las comprobaciones de errores en los argumentos.

Esta práctica puede causar problemas sutiles. Si declara funciones «a mano», en un fichero

puede cometer un error. Dado que el compilador sólo verá las declaraciones hechas a

Page 9: Ficheros y Cabeceras

mano en ese fichero, se adaptará al error. El programa enlazará correctamente, pero el uso

de la función en ese fichero será defectuoso. Se trata de un error difícil de encontrar, y que

se puede evitar fácilmente usando el fichero de cabecera correspondiente.

Si se colocan todas las declaraciones de funciones en un fichero de cabecera, y se incluye

ese fichero allí donde se use la función se asegurará una declaración consistente a través

del sistema completo. También se asegurará de que la declaración y la definición

corresponden incluyendo el fichero de cabecera en el fichero de definición.

Si declara una struct en un fichero de cabecera en C++, debe incluir ese fichero allí donde

se use una struct y también donde se definan los métodos de la struct. El compilador de

C++ devolverá un mensaje de error si intenta llamar a una función, o llamar o definir un

método, sin declararla primero. Imponiendo el uso apropiado de los ficheros de cabecera,

el lenguaje asegura la consistencia de las librerías, y reduce el número de error forzando

que se use la misma interface en todas partes.

El fichero de cabecera es un contrato entre el programador de la librería y el que la usa. El

contrato describe las estructuras de datos, expone los argumentos y valores de retorno

para las funciones. Dice, «Esto es lo que hace mi librería». El usuario necesita parte de esta

información para desarrollar la aplicación, y el compilador necesita toda ella para generar

el código correcto. El usuario de la struct simplemente incluye el fichero de cabecera, crea

objetos (instancias) de esa struct, y enlaza con el módulo objeto o librería (es decir, el

código compilado)

El compilador impone el contrato obligando a declarar todas las estruturas y funciones

antes que puedan ser usadas y, en el caso de métodos, antes de ser definidos. De ese

modo, se le obliga a poner las declaraciones en el fichero de cabecera e incluirlo en el

fichero en el que se definen los métodos y en los ficheros en los que se usen. Como se

Page 10: Ficheros y Cabeceras

incluye un único fichero que describe la librería para todo el sistema, el compilador puede

asegurar la consistencia y evitar errores.

Hay ciertos asuntos a los que debe prestar atención para organizar su código

apropiadamente y escribir ficheros de cabecera eficaces. La regla básica es «únicamente

declaraciones», es decir, sólo información para el compiladore pero nada que requiera

alojamiento en memoria ya sea generando código o creando variables. Esto es así porque

el fichero de cabecera normalmente se incluye en varias unidades de traducción en un

mismo proyecto, y si el almacenamiento para un identificador se pide en más de un sitio,

el enlazador indicará un error de definición múltiple (ésta es la regla de definición única de

C++: Se puede declarar tantas veces como se quiera, pero sólo puede haber una definición

real para cada cosa).

Esta norma no es completamente estricta. Si se define una variable que es «file static»

(que tiene visibilidad sólo en un fichero) dentro de un fichero de cabecera, habrá múltiples

instancias de ese dato a lo largo del proyecto, pero no causará un colisión en el enlazador

[51]. Básicamente, debe evitar cualquier cosa en los ficheros de cabecera que pueda

causar una ambigüedad en tiempo de enlazado.

Cómo usar #include en C y C++

La directiva de preprocesador #include se usa en los lenguajes C y C++para “incluir” las declaraciones de otro fichero en la compilación. Esta directiva no tiene más misterio para proyectos pequeños. En cambio, puede ayudar aprovechar bien esta directiva en proyectos con un gran número de subdirectorios.

El efecto de #includeCuando el preprocesador encuentra una línea #include "fichero", entonces reemplaza esta línea por el fichero incluido. Así procede con todas las directivas de inclusión – también en aquellas anidadas en los fichero ya a su vez incluidos. Es decir, existe un sólo fichero grande tras la precompilación.

Page 11: Ficheros y Cabeceras

No obstante, esta unión de varios ficheros no tiene lugar físicamente. Lo que sucede es que se interrumpe la compilación del fichero actual, se compila el fichero incluido y, tras compilarlo, se continúa con el primero. Por eso, el compilador puede decirnos, en qué fichero tuvo lugar un error de compilación.

En cambio, conviene tener esta idea del fichero único en mente, porque a veces ayuda a encontrar errores. Uno muy típico es olvidarse el punto y coma tras la declaración de una clase.

En este caso hay una declaración de clase en el fichero incluido:

class MiClase {}En el segundo puede haber algo así:

12

#include "mi_clase.h"MiClase miInstancia;

En este ejemplo, el compilador se quejará de que aquí no se puede definir un nuevo tipo en la línea de MiClase miInstancia aunque esta línea es correcta. El error verdadero es la falta del ; en el fichero incluido. Lo que el compilador realmente ve es

class MiClase {} MiClase miInstancia;No obstante, el programador no lo ve, porque el código está distribuido sobre dos fichero y el error se produce en el correcto.

La precompilación sólo modifica el código a nivel textual. No entiende del sintaxis del lenguaje. Por eso es posible distribuir el código de forma arbitraria. Por ejemplo, el siguiente ejemplo compilaría.

Fichero incluido

12

{ int

Fichero principal

1234

void main(void)#include "fichero_incluido" a;}

Esto es así porque el compilador ve el conunto

12345

void main(void){ int a;}

La posición del #include

Page 12: Ficheros y Cabeceras

Lo habitual es posicionar las inclusiones al inicio de cada fichero. Esto tiene sentido, porque se suele requerir declaraciones básices antes de declarar clases más complejas. Mi lugar preferido en los ficheros de cabecera es tras el guardián de inclusión múltiple.

1234567

// Guardian de inclusión múltiple#ifndef FICHERO_YA_INCLUIDO#define FICHERO_YA_INCLUIDO

#include "declaraciones_basicas.h"

#endifAsí se evita que un compilador poco sofisticado abre otra vez el mismo conjunto de ficheros cuando se inluye un fichero de cabecera dos o más veces.

En los ficheros de definición (los .c o .cpp), los guardianes de inclusión múltiple no hacen falta. No obstante, puede haber una restricción importante cuando se usan cabeceras precompiladas. En este caso, todos los ficheros fuente deben incluir primero el mismo fichero de cabecera – que es aquello que define la cabecera precompilada. El compilador de C++ de Borland permite varios ficheros de cabecera para la definición de una cabecera precompilada. Estas inclusiones deben ser lor primeros ficheros incluidos y se deben incluir en el mismo orden.

Puede darse el caso de no poner las inclusiones en el inicio de un fichero. Esto es frecuente en los fichero que se podrían denominar “programados en directivas de precompilación”. Normalmente se trata de ficheros de cabeceras con definiciones muy básicas como ajustes a la plataforma empleada. Por ejemplo, en medio de un fichero “definiciones_basicas.h” puede haber unas líneas

123456789

#if PLATAFORMA_ES_LINUX#include "funcionalidad_gratis.h"#elif PLATAFORMA_ES_MICROSOFT_WINDOWS#include "funcionalidad_cara.h"#elif PLATAFORMA_ES_APPLE#include "funcionalidad_muy_cara.h"#else#error Esta plataforma no está soportada#endif

La diferencia entre “” y <>La directiva #include existe en dos versiones. En una se pone el nombre de fichero entre comillas, en la otra entre paréntesis angulares (el signo menor y mayor como “comillas”).

12

#include "fichero_con_comillas.h"#include <fichero_entre_menor_y_mayor.h>

Page 13: Ficheros y Cabeceras

La versión con los paréntesis angulares busca los ficheros en todos los directorios que se han especificado en la llamada al compilador – normalmente con la opción “-I”. Estos directorios se suelen rastrear por el fichero incluido en el orden en que aparecen en la línea de comando.

Cuando se incluye un fichero entre comillas, entonces el compilador busca este fichero primero en el mismo directorio que el fichero actualemente compilado y después en los demás directorios. Es decir, la versión con comillas se diferencia de la versión con paréntesis angulares únicamente por buscar primero en el directorio del fichero compilado. Tras no encontrarlo ahí actúa igual.

Esto muchas veces no es ninguna diferencia, ya que se suelen especificar todos los directorios en la línea de comando del compilador. Así no se suele dar el caso que se puede incluir un fichero con comillas pero no con paréntesis angulares.

Más significativo es el comportamiento ante ficheros con el mismo nombre en distintos directorios. En este caso la versión con comillas da preferencia sobre el fichero en el mismo directorio y esto suele ser el mejor acertado. Aunque sea preferible nombrar ficheros de forma única en un proyecto, es posible que no se pueda evitar tener dos ficheros con el mismo nombre cuando se incluyen varias bibliotecas de terceros.

De ahí se puede deducir que es imperativo incluir cabeceras de la misma biblioteca con comillas. De esta forma se puede asegurar que las cabeceras de una biblioteca se incluyan entre si aunque haya otros con el mismo nombre en uno de los directorios especificados en la línea de comandos.

Además, incluir con comillas puede dar al programador un significado adicional: que este fichero está bajo la custodia de mi equipo de desarrollo. Las cabeceras incluidas con paréntesis angulares son de bibliotecas de terceros. Los primeros ficheros puedo modificar si hace falta, los segundos no.

El orden de las inclusionesEl orden de las directivas #include no importa cuando todos los identificadores del programa son únicos. No obstante, a veces no lo son yconviene generar el error “este identificador ya existe” en nuestro código y no en el código de una biblioteca estándar.

Esto se consigue incluyendo primero las caberas de terceros. Si aparece un error de identificador doble, entonces aparece en la segunda definición – que es la nuestra – y ahí podemos cambiar el nombre del objeto sin problema.

En proyectos realmente grandes puede haber varias bibliotecas de distintas dependencias. Por la misma razón de generar los errores de identificadores dobles en el código más fácilmente modificable, conviene incluir las bibliotecas más básicas primero. Dentro del mismo nivel podemos ordenar los ficheros incluidos de forma alfabética. Esto ayuda a encontrar inclusiones que faltan o sobran.

Page 14: Ficheros y Cabeceras

El siguiente código muestra una secuencia de inclusiones para un fichero “definicion_de_mi_clase.cpp”.

123456789101112131415161718192021222324

// Primero se debe incluir la cabecera de precompilación#include "cabecera_de_precompilacion.h"

// Segundo, incluir la cabecera correspondiente// a este fichero de implementación.// Esto deja más claro, que es la// clase que se implementa aquí.#include "definicion_de_mi_clase.h"

// A continuación inclusiones de la biblioteca estándar.// Se usan paréntesis angulares.#include <vector>

// Inclusiones de otras bibliotecas de terceros#include <wx.h>#include <gl.h>

// Inclusiones de subbibliotecas básicas de// mi proyecto con comillas#include "mis_definiciones_basicas.h"

// Luego las demás inclusiones de mi proyecto#include "clases_auxiliares.h"#include "más_definiciones_para_mi_clase.h"

Usar rutas relativasUna forma de evitar nombres de fichero dobles es incluir ficheros con rutas relativas.

12

#include "definiciones/tipos_básicos.h"#include "funcionalidad/tipos_básicos.h"

La desventaja de esta forma es, que uno debe saber, en qué directorio se encuentra cada cabecera. No obstante, esto suele ser un problema menor. Sin ruta relativa, uno debería poner un prefijo a cada nombre de fichero para evitar nombres dobles. Estos prefijos son típicamente los nombres de los directorios. Es decir, todos los ficheros en el directorio “definiciones” tienen se llaman “definiciones_xxx”. Al final debo saber el nombre de directorio de todas formas.

Los programadores de C++ han copiado de Java la idea de estructurar los directorios como los espacios de nombre. Así, el uso de la claseNombreDeEspacio::Subespacio::MiClase requiere la inclusión del fichero “nombre_de_espacio/subespacio/mi_clase.h”.

Page 15: Ficheros y Cabeceras

El uso de rutas relativas en las inclusiones puede mejorar bastante el orden y reducir la configuración del compilador. Así basta incluir un solo directorio para obtener acceso a todos los componentes de la biblioteca boost, por ejemplo. En cambio, rutas relativas hacen más complicado recolocar ficheros a otros directorios. Y esto puede pasar a menudo en fases tempranos de un proyecto.

Optimizar la velocidad de compilaciónLa inclusión de un sólo fichero normalmente no afecta mucho al tiempo, que el compilador requiere para la compilación de un fichero. Pero esto se cambia si el fichero incluido incluye más ficheros que a su vez incluyen aún más ficheros. Gracias a tantas inclusiones anidadas, una sola inclusión, sí, puede cambiar el tiempo de compilación drásticamente.

Una forma de mejorar la velocidad es utilizar cabeceras precompiladas. Estas cabeceras deben estar incluidas por todos los ficheros de definición y, por eso, debe ser una decisión temprana en un proyecto de utilizarlas o no – al menos si uno quiere evitar añadir esta inclusión en muchos ficheros.

Otra forma es evitar la inclusión de ficheros no necesarios. Normalmente una inclusión adicional no perjudica al resultado de la compilación. Simplemente añade declaraciones que finalmente no se usan. Pero la compilación de estas declaraciones cuesta un tiempo que nos queremos ahorrar.

Para proyectos grandes puede convenir no incluir definiciones de tipos que sólo aparecen como punteros o referencias en los ficheros de cabecera. Esto tipos se pueden introducir con una declaración forward. Sólo en el fichero de definición se incluye entonces la cabecera que define este tipo.

Como ejemplo damos una clase, que guarda un puntero a otra clase. En el fichero de cabecera tenemos

12345678

// Declaración forwardclass OtraClase;

// Declaración de la clase principalclass UnaClase{ OtraClase* dame_un_puntero_a_otra_clase();};

En el fichero de definición tenemos

1234

#include "una_clase.h"#include "otra_clase.h"

OtraClase* UnaClase::dame_un_puntero_a_otra_clase()

Page 16: Ficheros y Cabeceras

5678

{ static OtraClase otraInstancia; return &otraInstancia;}

Cabe señalar que una declaración forward debe tener lugar dentro del nombre de espacio correspondiente.

1234567891011

// Esto no es correctoclass NombreDeEspacio::Subespacio::Clase;

// Se debe hacer la declaración forward asínamespace NombreDeEspacio{ namespace Subespacio { class Clase; }}

ConclusiónComo hemos comprobado, hay bastante que se puede tener en cuenta sobre la directiva #include, cuando se trabaja en proyectos grandes.

La inclusión genera un sólo fichero grande. Tener esto en mente cuando se produce un error de compilación en la primera línea aparamente correcta tras un #include.

Conviene incluir ficheros del propio proyecto con comillas y las cabeceras de terceros con paréntesis angulares para marcar la diferencia entre “propio” y “estándar”.

Ordenando las propias inclusiones al final ayuda a corregir errores de doble definición de identificadores.

Rutas relativas son una buena opción para proyectos con una clara estructura de directorios.

Usar cabeceras precompiladas y evitar incluir ficheros innecesarios – también aprovechando declaraciones forward – puede acelerar el proceso de compilación.