2011-01-21 18 views
24

Hola y gracias por leer. Esto podría ser simplemente pertenecientes a la categoría de las 'preferencias personales pero de todos modos aquí vamos ...Defining constructor en archivo de cabecera VS implementación (.cpp) archivo

puedo definir el cuerpo de un constructor de clase de la clase de archivos .h o en el archivo de implementación .cpp . Estos dos estilos son probablemente idénticos en lo que respecta al compilador dentro de un proyecto específico (proyecto para mí significa dll). Lo mismo se aplica realmente a las funciones de los miembros: se pueden definir en el archivo de encabezado o simplemente declararse allí y luego definirse en el archivo cpp.

Sin embargo ...

He descubierto que si tengo que incluir el archivo de cabecera dicha clase (s) en diferentes proyectos (lo que significa que en última instancia, el código que utiliza el archivo de cabecera termina en un diferente DLL) luego tener la implementación real en el archivo de encabezado causa algunos dolores de cabeza en la compilación (no en la vinculación ... ni siquiera llego a ese punto). ¿Por qué? Bueno, no voy a ir demasiado en detalle, pero el compilador, obviamente, trata de resolver todas las funciones que podrían definirse en otros archivos de encabezado, etc ... forzando al desarrollador deficiente a comenzar a tirar varios archivos de encabezado eetc ...

LONGSTORY corto:

¿No es siempre mejor mantener los archivos de encabezado libres de cualquier implementación y simplemente usarlos para 'declaraciones'? Eso haría que sea más fácil incluirlos en más de un proyecto sin tener que llevar una gran cantidad de basura adicional.

¿Cuál es su opinión al respecto? Gracias!

Respuesta

21

Mantenga sus encabezados libres de implementaciones, a menos que desee que las implementaciones estén en línea (por ejemplo, getters/setters triviales). Y a menos que sean plantillas, por supuesto.

No veo ninguna razón para hacer una excepción para los constructores. Póngalos en el archivo .cpp.

+1

Incluso hay instancias en las que puede poner una implementación en el encabezado. Digamos que si dos archivos diferentes debían incluirse entre sí, eso no se puede hacer en el encabezado, debe hacerse en el cpp. –

+0

Quiere decir "no puedo", no "puede". De lo contrario, un buen punto. – Thomas

+2

La excepción 'trivial' se puede aplicar tan fácilmente a un constructor trivial. No hay razón para tratarlos de manera diferente o para no incluirlos también. –

17

Un punto importante a tener en cuenta es que si una función miembro está definida dentro de un archivo de encabezado, debe estar dentro del cuerpo de la clase o debe marcarse explícitamente como inline. En otras palabras, es simplemente erróneo de hacer esto en un archivo de cabecera:

class A { 
    public: 
    A(); 
}; 

A::A() { 
    // constructor body 
} 

La razón por la que está mal es porque hará que el compilador incluye la definición en cada unidad de compilación, mientras que es obvio que cualquier función debe ser definido solo una vez Aquí hay formas correctas de hacer la misma cosa:

class A { 
    public: 
    inline A(); 
}; 

inline A::A() { 
    // constructor body 
} 

O:

class A { 
    public: 
    inline A() { // inline isn't required here, but it's a good style 
    // constructor body 
    } 
}; 

En ambos casos, el constructor está en línea. La única forma correcta de convertirlo en una función fuera de línea regular sería definirlo en el archivo de implementación, no en el encabezado. Esta es la diferencia más importante entre estos dos enfoques.

Ahora, vale la pena señalar que la optimización es una optimización. Y como siempre con optimizaciones, es mejor evitarlas hasta que sea necesario.Entre otros problemas que pueden surgir, existe el problema de compatibilidad: si cambia el cuerpo de una función que no está en línea, solo necesita recompilar la unidad donde está definida, y todos comienzan a usar la nueva implementación de inmediato. Con las funciones inline, necesita recompilar cada unidad que incluye el encabezado relevante, lo que puede ser un problema, especialmente si el encabezado se usa en diferentes proyectos por diferentes personas.

En otras palabras, use definiciones fuera de línea regulares siempre que sea posible hasta que se demuestre mediante un perfil que una llamada de función particular es un cuello de botella de rendimiento. La única excepción razonable a esta regla son los setters y getters triviales, e incluso con ellos es mejor tener cuidado: un día pueden volverse no triviales y significará una gran cantidad de compilación y ruptura de compatibilidad.

1

Otra nota a tener en cuenta: cualquier cambio en un archivo de encabezado requiere la reconstrucción de todos los archivos que incluyen ese archivo de encabezado. La mayoría de los sistemas de compilación reconstruirán los archivos de origen (* .cpp/.cc) que dependen del archivo de encabezado modificado.

Si cambia un método de una clase definida en un archivo de encabezado, se reconstruirán todos los archivos de fuentes, incluido el archivo de encabezado. Si cambia un método en un archivo fuente, solo se reconstruye el archivo fuente. Esto podría ser un problema para proyectos de medianos a grandes.

Para simplificar el proceso de compilación, la mayoría de los métodos de una clase se deben definir en un archivo de origen. Los métodos pequeños y otros candidatos para inline se deben definir en el archivo de encabezado.

Cuestiones relacionadas