their includes headers guards functions files cpp and c++ file include header

includes - include class c++



¿Limpiar sus#incluir declaraciones? (12)

¿Cómo mantiene las declaraciones #include en su proyecto C o C ++? Parece casi inevitable que con el tiempo el conjunto de declaraciones de inclusión en un archivo sea insuficiente (pero que funcione debido al estado actual del proyecto) o incluya cosas que ya no son necesarias.

¿Has creado alguna herramienta para detectar o rectificar problemas? ¿Alguna sugerencia?

He estado pensando en escribir algo que compile cada archivo sin encabezado individualmente muchas veces, cada vez que elimine una instrucción #include. Continúe haciendo esto hasta que se logre un conjunto mínimo de inclusiones.

Para verificar que los archivos de encabezado incluyan todo lo que necesitan, crearía un archivo de origen que lo único que hace es incluir un archivo de encabezado e intentar compilarlo. Si la compilación falla, entonces al archivo de encabezado le falta una inclusión.

Antes de crear algo, pensé que debía preguntar aquí. Esto parece un problema un tanto universal.


He estado pensando en escribir algo que compile cada archivo sin encabezado individualmente muchas veces, cada vez que elimine una instrucción #include. Continúe haciendo esto hasta que se logre un conjunto mínimo de inclusiones.

Creo que esto está mal orientado, y dará lugar a conjuntos de inclusión "insuficientes pero que simplemente funcionan".

Supongamos que su archivo de origen utiliza numeric_limits , pero también incluye algún archivo de encabezado, que por razones propias incluye <limits> . Eso no significa que su archivo fuente no deba incluir <limits> . Ese otro archivo de encabezado probablemente no está documentado para definir todo lo definido en <limits> , por casualidad lo hace. Es posible que algún día se detenga: tal vez solo use un valor como parámetro predeterminado de alguna función, y tal vez ese valor predeterminado cambie de std::numeric_limits<T>::min() a 0. Y ahora su archivo de origen no lo hace compile más, y el mantenedor de ese archivo de cabecera ni siquiera sabía que su archivo existía hasta que rompió su compilación.

A menos que tenga problemas de generación paralizantes en este momento, creo que la mejor forma de eliminar los complementos redundantes es simplemente adquirir el hábito de revisar la lista cada vez que toca un archivo para realizar el mantenimiento. Si descubre que tiene docenas de inclusión y después de haber revisado el archivo, aún no puede descubrir para qué sirve cada uno, considere dividirlo en archivos más pequeños.


Para verificar que los archivos de encabezado incluyan todo lo que necesitan, crearía un archivo de origen que lo único que hace es incluir un archivo de encabezado y tratar de compilarlo. Si la compilación falla, entonces al archivo de encabezado le falta una inclusión.

Obtendrá el mismo efecto al hacer la siguiente regla: que el primer archivo de encabezado que foo .c o foo .cpp debe incluir debe ser el nombre correspondiente foo .h. Hacer esto asegura que foo .h incluya todo lo que necesita compilar.

Además, el libro de Lakos, el diseño de software C ++ a gran escala (por ejemplo) enumera muchas, muchas técnicas para mover los detalles de la implementación de un encabezado al archivo CPP correspondiente. Si lo lleva al extremo, utilizando técnicas como Cheshire Cat (que oculta todos los detalles de la implementación) y Factory (que oculta la existencia de subclases), muchos encabezados serían independientes sin incluir otros encabezados, y en cambio se conformarían con solo declaración de reenvío a tipos opacos en su lugar ... excepto quizás para clases de plantilla.

Al final, cada archivo de encabezado debe incluir:

  • No hay archivos de encabezado para tipos que son miembros de datos (en su lugar, los miembros de datos se definen / ocultan en el archivo CPP usando la técnica "cheshire cat" alias "pimpl")

  • No hay archivos de encabezado para tipos que son parámetros para o devuelven tipos de métodos (en su lugar, son tipos predefinidos como int ; o, si son tipos definidos por el usuario, son referencias, en cuyo caso un tipo opaco declarado hacia adelante declaración como simplemente class Foo; lugar de #include "foo.h" en el archivo de encabezado es suficiente).

Lo que necesitas entonces es el archivo de encabezado para:

  • La superclase, si esta es una subclase.

  • Posiblemente cualquier tipo de plantilla que se use como parámetros de método y / o tipos de retorno: al parecer, se supone que también debe poder reenviar las clases de plantilla, pero algunas implementaciones de compiladores pueden tener un problema con eso (aunque también puede encapsular cualquier plantilla por ejemplo, List<X> como detalles de implementación de un tipo definido por el usuario, por ejemplo, ListX ).

En la práctica, podría crear un "standard.h" que incluya todos los archivos del sistema (por ejemplo, encabezados STL, tipos específicos de O / S y / o cualquier #define s, etc.) que sea usado por cualquier / todos los archivos de encabezado en el proyecto, e incluya eso como el primer encabezado en cada archivo de encabezado de la aplicación (e indique al compilador que trate este "standard.h" como el ''archivo de encabezado precompilado'').

//contents of foo.h #ifndef INC_FOO_H //or #pragma once #define INC_FOO_H #include "standard.h" class Foo { public: //methods ... Foo-specific methods here ... private: //data struct Impl; Impl* m_impl; }; #endif//INC_FOO_H

//contents of foo.cpp #include "foo.h" #include "bar.h" Foo::Foo() { m_impl = new Impl(); } struct Foo::Impl { Bar m_bar; ... etc ... }; ... etc ...


Dependiendo del tamaño de su proyecto, puede ser útil observar los gráficos de inclusión creados por Doxygen (con la opción INCLUDE_GRAPH ).


Echa un vistazo al proyecto cppclean . Aunque aún no han implementado esa función, pero está planeado que se haga.

Desde el sitio del proyecto:

CppClean intenta encontrar problemas en la fuente de C ++ que ralentizan el desarrollo, especialmente en grandes bases de código. Es similar a la pelusa; sin embargo, CppClean se enfoca en encontrar problemas globales entre módulos en lugar de problemas locales similares a otras herramientas de análisis estático.

El objetivo es encontrar problemas que ralentizan el desarrollo en grandes bases de código que se modifican con el tiempo dejando el código no utilizado. Este código puede venir en muchas formas, desde funciones, métodos, miembros de datos, tipos, etc. no utilizados hasta directivas #include innecesarias. Los #includes innecesarios pueden causar compilaciones adicionales considerables, lo que aumenta el ciclo de edición-compilación-ejecución.

Y particularmente en la característica #include:

  • (planeado) Encuentra archivos de encabezado innecesarios #incluidos
    • No hay referencia directa a nada en el encabezado.
    • El encabezado es innecesario si las clases fueron declaradas en su lugar.
  • (planificado) Los archivos de origen que hacen referencia a los encabezados no están directamente incluidos #, es decir, los archivos que se basan en un #include transitivo de otro encabezado

Here puedes encontrar un espejo en BitBucket.


En cuanto a las herramientas, he usado Imagix (esto fue hace aproximadamente 6 años) en las ventanas para identificar las que no son necesarias, así como las que son necesarias, pero se incluyen indirectamente a través de otra inclusión.


Generalmente creo un archivo de origen (main.c, por ejemplo) y un archivo de encabezado para ese archivo de origen (main.h). En el archivo de origen, pongo todo el tipo principal de funciones de "interfaz" que uso en ese archivo (en general, sería main() ), y luego cualquier función que obtenga después de refactorizar esas funciones (detalles de implementación), bajar. En el archivo de encabezado, declaro algunas funciones extern que están definidas en otros archivos de origen, pero que se usan en el archivo de origen que usa ese encabezado. Luego declaro las estructuras u otros tipos de datos que uso en ese archivo fuente.

Luego simplemente compilo y vinculo a todos juntos. Se mantiene agradable y limpio. Una sección típica de inclusión ... en mi proyecto actual se ve así

#include<windows.h> #include<windowsx.h> #include<stdio.h> #include"interface.h" #include"thissourcefile.h" //function prototypes //source

hay un encabezado de interface que realiza un seguimiento de las estructuras de datos que utilizo en todos los formularios del proyecto, y luego thissourcefile.h que hace exactamente lo que acabo de explicar (declara externs , etc.).

Además, nunca defino nada en mis encabezados, solo pongo declaraciones allí. De esa manera pueden ser incluidos por diferentes archivos de origen y aún así vincularse exitosamente. Los prototipos de funciones (extern, estáticos o de otro tipo) y las declaraciones van en el encabezado, de esa manera se pueden usar muchas veces; las definiciones van en la fuente, porque solo tienen que estar en un lugar.

Obviamente, esto sería diferente si estuvieras creando una biblioteca, o algo así. Pero solo para la vinculación interna de proyectos, encuentro que esto mantiene todo limpio y agradable. Además, si escribe un makefile (o si solo usa un IDE), la compilación es realmente simple y eficiente.


La detección de inclusiones superfluas ya se ha discutido en esta pregunta .

No tengo conocimiento de ninguna herramienta que ayude a detectar las insuficientes ofertas de trabajo en el trabajo, pero las buenas convenciones de codificación pueden ayudar aquí. Por ejemplo, la Guía de estilo de Google C ++ exige lo siguiente, con el objetivo de reducir las dependencias ocultas:

En dir/foo.cc , cuyo propósito principal es implementar o probar las cosas en dir2/foo2.h , ordene su incluye de la siguiente manera:

  1. dir2/foo2.h (ubicación preferida, consulte los detalles a continuación).
  2. C archivos del sistema.
  3. Archivos del sistema C ++.
  4. Otras bibliotecas de archivos .h.
  5. Los archivos .h de tu proyecto.

Si está codificando en Eclipse con CDT, puede usar el comando Organizar Incluye. Simplemente presione Ctrl + Shift + O y agregará los necesarios incluidos y eliminará los innecesarios.


Si usa el compilador de Visual Studio, puede probar la opción del compilador / showIncludes y luego analizar lo que emite a stderr. MSDN: "Hace que el compilador muestre una lista de los archivos de inclusión. Los archivos de inclusión anidados también se muestran (archivos que se incluyen de los archivos que usted incluye)".


Sip. Tenemos un preprocesador propio que nos da acceso a nuestro propio lenguaje de macros. También verifica que los archivos de encabezado solo se incluyan una vez. La creación de un preprocesador simple que compruebe múltiples inclusiones debería ser bastante fácil.


Tengo el hábito de ordenar mi inclusión desde el nivel de abstracción alto al nivel de abstracción bajo. Esto requiere que los encabezados tengan que ser autosuficientes y que las dependencias ocultas se revelen rápidamente como errores del compilador.

Por ejemplo, una clase ''Tetris'' tiene un archivo Tetris.h y Tetris.cpp. La orden de inclusión para Tetris.cpp sería

#include "Tetris.h" // corresponding header first #include "Block.h" // ..then application level includes #include "Utils/Grid.h" // ..then library dependencies #include <vector> // ..then stl #include <windows.h> // ..then system includes

Y ahora me doy cuenta de que esto realmente no responde a tu pregunta, ya que este sistema no ayuda realmente a limpiar las necesidades innecesarias. Ah bueno..


Un gran problema con la técnica de eliminar un encabezado y una recompilación es que puede llevar a un código aún compilado, pero incorrecto o ineficiente.

  1. Especialización de plantillas: si tiene una especialización de plantillas para un tipo específico que se encuentra en un encabezado y la plantilla más general en otra, la eliminación de la especialización puede dejar el código en un estado compilable, pero con resultados no deseados.

  2. Resolución de sobrecarga: un problema similar: si tiene dos sobrecargas de una función en encabezados diferentes, pero eso toma algunos tipos compatibles, puede terminar eliminando la versión que mejor se adapta a un caso, pero aún así tiene el código compilado. Esto es probablemente menos probable que la versión de especialización de plantillas, pero es posible.