2010-04-26 6 views
6

Soy bastante nuevo en C++, pero tengo entendido que una sentencia #include esencialmente solo volcará el contenido del archivo #incluido en la ubicación de esa declaración. Esto significa que si tengo un número de declaraciones '#include' y 'using' en mi archivo de cabecera, mi archivo de implementación puede simplemente #incluir el archivo de encabezado, y al compilador no le importará si no repito las otras declaraciones .¿Deberían repetirse las sentencias '#include' y 'using' tanto en el encabezado como en los archivos de implementación (C++)?

¿Qué pasa con las personas?

Mi principal preocupación es que si no repito las instrucciones '#include', 'using' y también 'typedef' (ahora que lo pienso), quita esa información del archivo en el que se usa, lo que podría generar confusión.

Estoy trabajando en pequeños proyectos en este momento en el que realmente no causará ningún problema, pero me imagino que en proyectos más grandes con más gente trabajando en ellos podría convertirse en un problema importante.

Un ejemplo es el siguiente:

ACTUALIZACIÓN: mis prototipos de las funciones de 'Unidad' tienen cuerda, ostream y StringSet entre sus tipos de retorno y parámetros - No estoy incluyendo cualquier cosa en mi archivo de cabecera que se utiliza sólo en el archivo de implementación.

//Unit.h 

#include <string> 
#include <ostream> 
#include "StringSet.h" 

using std::string; 
using std::ostream; 

class Unit { 

public: 
    //public members with string, ostream and StringSet 
    //in their return values/parameter lists 
private: 
    //private members 
    //unrelated side-question: should private members 
    //even be included in the header file? 
} ; 


//Unit.cpp 

#include "Unit.h" 

//The following are all redundant from a compiler perspective: 
#include <string> 
#include <ostream> 
#include "StringSet.h" 

using std::string; 
using std::ostream; 

//implementation goes here 
+0

Una pregunta sin relación que no quería complicar la pregunta principal es, ya que todos parecen decir que las declaraciones globales de 'uso' son una mala idea, ¿cómo puedo tener una clase 'usando std :: string' ¿o algo? Mover esa instrucción a la declaración de clase provoca un error de compilación porque no le gustan los espacios de nombres en una instrucción 'using' en esa ubicación. –

+2

¿Qué hay de malo en decir explícitamente 'std :: string'? –

+0

nada hasta que comienza a causar que las líneas se rompan cuando de otro modo no lo harían, lo que reduce la legibilidad. Alejémonos un poco de std :: por un tiempo y digamos que quería usar el nombre deLongestNamespeceName en TheKnownUniverse :: string - solo para evitar el mantra común 'siempre debería escribir std :: string' (ya que lo escuché muchas veces ya, y aunque es un punto válido, realmente no ayuda con la pregunta en cuestión). –

Respuesta

6

Un utilizando directiva (using namespace std;) no debe residir en un encabezado menos que esté contenida dentro de una función. Es una mala práctica Es poco probable que todos los usuarios de su encabezado busquen búsquedas no calificadas para todo en un espacio de nombres determinado; la inclusión de encabezados no relacionados puede provocar una ambigüedad inesperada y fallas de compilación. Personalmente, evito el using-directive dentro de las funciones por el mismo razonamiento, pero esto generalmente se considera menos dañino.

A tipo de alias (ya sea a través typedef std::string string; o using string = std::string;) deben usarse con cuidado. Las definiciones de tipo tienen un significado, por lo que nunca debe volver a declararlo. Por ejemplo, este es un error:

typedef int myint; 
typedef float myint; 

debido a tipos conflictivos.

Un mediante declaración- (using std::string; o using std::memcpy;) hace que un símbolo accesible para unqualified name lookup. Es extremadamente útil al obtener el argument-dependent lookup correcto, que generalmente no importa a menos que esté escribiendo una biblioteca. El consejo es diferente dependiendo de si trae un tipo o una función. Piense en using-declaration s con tipos de la misma manera que tipo alias: No tiene sentido tener múltiples definiciones bajo el mismo nombre. Con funciones, todo lo que realmente está haciendo es ampliar la resolución de sobrecarga para incluir algunas cosas más (aunque generalmente no es necesario).

// Finding multiple operator<< functions makes sense 
using std::operator<<; 
using mylib::operator<<; 

// Finding multiple string classes does not make sense 
using std::string; 
using mylib::string; 

Para repetir #include, se debe considerar si realmente es necesario incluir el archivo en el encabezado en el primer lugar. Tal vez un forward declaration se adapte a sus necesidades.

+0

He actualizado la pregunta para aclarar: evito tener #includes en mi archivo de encabezado a menos que realmente los necesite en el archivo de encabezado. No estoy seguro de si el ejemplo de declaración directa se aplica si uso std :: string u otra clase que no estoy definiendo. ¿Pueden aclarar cómo funcionaría esto si fuera así? –

+0

El problema con 'std :: string' es que realmente es simplemente' typedef basic_string string' ... el estándar declara: 'No está definido para un programa C++ agregar declaraciones o definiciones al espacio de nombres std o namespaces dentro del espacio de nombres std a menos que se especifique lo contrario. "Entonces ... no puedes reenviar-declararlo". En cuanto a la pregunta 'really_long_namespace_name', aquí es donde las personas usan tipos de miembros. 'std :: allocator' es un buen ejemplo de esto: http://www.cplusplus.com/reference/std/memory/allocator/ –

+0

En C++ 11' using' también se puede usar para las declaraciones tipo alias, que de acuerdo con el estándar es equivalente a un typedef. Y dado que typedef está bien en los encabezados, el mensaje _ "Nunca debería tener una instrucción de uso en un encabezado a menos que esté dentro de una función". _ Ya no parece verdadero. – Zitrax

0

Mantenga los archivos de encabezado al mínimo. Esto significa tan poco como sea posible. el .El archivo cpp generalmente incluirá el encabezado correspondiente y cualquier otro encabezado necesario para la implementación.

0

Como dijo Travis, no debería tener using declaraciones en un archivo de encabezado porque eso significa que se incluirán en todas las unidades de traducción que incluyen ese archivo de encabezado, lo que puede causar problemas confusos.

Si solo requiero la funcionalidad de un archivo de encabezado en un archivo cpp, solo lo incluyo en ese archivo cpp. Es una buena práctica para proyectos más grandes porque significa menos trabajo para el compilador. Además, siempre que sea posible, utilizo declaraciones directas en los encabezados en lugar de incluir (y de nuevo, incluir los encabezados en el archivo cpp).

0

Se considera una mala forma tener una declaración using en un archivo de encabezado, a menos que esté duplicando intencionadamente un símbolo en un espacio de nombres diferente. Está bien usarlo en un archivo cpp.

Cada typedef debería existir solo una vez en su código base. Eso debería estar en un archivo de encabezado si necesita ser utilizado en múltiples archivos cpp/h. Duplicarlos te causará mucho dolor.

Un archivo de encabezado debe tener todas las declaraciones #include que necesita y ninguna otra. Si solo se mencionan los punteros a una clase, utilice una declaración directa en lugar de incluir el encabezado. Cualquier otra inclusión que se requiera solo dentro del archivo cpp debería ir allí. La repetición de las inclusiones desde el encabezado está bien, pero no es obligatorio. Es solo una elección de estilo.

3
  • Sólo se incluyen en un encabezado y/o fuente de lo que realmente necesita (si las declaraciones prospectivas están disponibles, y suficiente, entonces declaran hacia adelante en lugar de incluir)
  • No utilice using afirmación en las cabeceras de (a menos que dentro de los ámbitos de funciones) ... Añadir using en el encabezado contaminará los espacios de nombres de todas las fuentes, incluido el encabezado.
  • Debe asegurarse de que cada archivo (encabezado de la fuente) incluya todo lo que necesita, y nada más.

No es necesario que se preocupe si algunos incluyen son redundantes. Las protecciones de encabezado y las optimizaciones del precompilador están ahí para manejarlo por usted.

Debe poder manipular cada archivo de forma aislada.

Por ejemplo, supongamos que usa std::string en el encabezado y en la fuente, pero, como una "optimización", solo incluyó la cadena en el encabezado ... Si descubre más tarde, no necesita más la cadena en el encabezado, y desea eliminarla (limpieza del código, y todo ...), tendrá que modificar la fuente para incluir la cadena. Ahora, imaginemos que tiene DIEZ fuentes, incluido el encabezado ...

Ahora, por supuesto, puede tener excepciones a esta regla (por ejemplo, encabezados precompilados, o incluso encabezados, el único objetivo es hacer múltiples incluye como un cortesía), pero de forma predeterminada, debe tener un encabezado autosuficiente y archivos de origen (es decir, archivos que incluyan todo lo que utilizan, ni más ni menos).

Cuestiones relacionadas