2009-01-17 19 views
54

Tengo una clase que tiene muchas funciones pequeñas. Por funciones pequeñas, me refiero a funciones que no procesan sino que simplemente devuelven un valor literal. Algo así como:Definición de funciones de escritura en archivos de encabezado en C++

string Foo::method() const{ 
    return "A"; 
} 

He creado un archivo de cabecera "foo.h" y el archivo de origen "foo.cpp". Pero dado que la función es muy pequeña, estoy pensando en ponerla en el archivo de encabezado. Tengo las siguientes preguntas:

  1. ¿Hay algún problema de rendimiento u otro problema si incluyo esta definición de función en el archivo de encabezado? Tendré muchas funciones como esta.
  2. Según tengo entendido, cuando la compilación finalice, el compilador expandirá el archivo de encabezado y lo colocará donde está incluido. ¿Es eso correcto?

Respuesta

61

Si la función es pequeña (la posibilidad de que iba a cambiar con frecuencia es baja), y si la función se puede poner en la cabecera sin incluir miríadas de otras cabeceras (debido a que su función depende de ellos), se es perfectamente válido para hacerlo. Si se declaran en línea externo, entonces se requiere el compilador para darle la misma dirección por cada unidad de compilación:

headera.h:

inline string method() { 
    return something; 
} 

funciones miembros están en línea implícita siempre que se definen en el interior su clase. Lo mismo es cierto para ellos: si pueden colocarse en el encabezado sin complicaciones, de hecho puede hacerlo.

Como el código de la función se coloca en el encabezado y es visible, el compilador puede hacer llamadas en línea, es decir, poner el código de la función directamente en el sitio de la llamada (no tanto porque lo puso en línea antes pero más porque el compilador decide de esa manera. Poner en línea solo es una pista para el compilador sobre eso). Eso puede dar como resultado una mejora en el rendimiento, porque el compilador ahora ve donde los argumentos coinciden con las variables locales para la función, y donde el argumento no se alía entre sí, y por último pero no menos importante, la asignación del marco de función ya no es necesaria.

Según tengo entendido, cuando la compilación finalice, el compilador expandirá el archivo de encabezado y lo colocará donde está incluido. ¿Es eso correcto?

Sí, eso es correcto.La función se definirá en cada lugar donde incluya su encabezado. Al compilador le importará poner solo una instancia del mismo en el programa resultante, eliminando los demás.

+0

Gracias. Todas estas pequeñas funciones son virtuales. ¿Eso marcará alguna diferencia en la alineación? Y creo que escribir el cuerpo de la función en el archivo fuente y marcar como en línea es mejor que escribir directamente en el encabezado. Me temo que el archivo de encabezado será menos legible si todas estas funciones están definidas allí. –

+0

si el compilador puede encontrar la dirección de una llamada de función virtual, también puede alinear: b * b_ = new d; doit (b_); // si inserta doit, verá que b_ es d. entonces podría alinear el código de la definición de la función virtual tal como está en d. virtual hace que sea más difícil, pero no imposible, –

+0

pero estoy de acuerdo con usted: a menudo recuerdo que coloco el código en los encabezados, porque cuando lo cambio afecta a todo el código que lo ha llamado, y con frecuencia definir en los encabezados requiere incluir al menos otro encabezado del que depende el código. (no siempre para los getters simples, los pongo allí). –

4

Habrá un aumento en el rendimiento porque la implementación en los archivos de encabezado está implícitamente en línea. Como mencionó que sus funciones son pequeñas, la operación en línea será muy beneficiosa para usted en mi humilde opinión.

Lo que usted dice acerca del compilador es también true.There hay diferencia para el compilador — que no sea inlining — entre el código en el archivo de cabecera o .cpp archivo.

2
  1. Si sus funciones son tan simples, que sean en línea, y usted tiene que pegarse en el archivo de cabecera de todos modos. Aparte de eso, cualquier convención es solo eso: convenciones.

  2. Sí, el compilador expande el archivo de encabezado donde encuentra las instrucciones #include.

1

Depende de las normas de codificación que se aplican en su caso, pero:

funciones pequeñas y sin bucles y todo lo demás debe ser inline para un mejor rendimiento (pero un poco más grande de código - importante para algunas aplicaciones limitadas o integrado).

Si tiene el cuerpo de la función en el encabezado, lo tendrá por defecto en línea (d) (lo cual es bueno cuando se trata de velocidad).

Antes de que el archivo objeto sea creado por el compilador, se llama al preprocesador (opción -E para gcc) y el resultado se envía al compilador que crea el objeto fuera del código.

Así que la respuesta corta es:

- Declaración de funciones en la cabecera es bueno para la velocidad (pero no para el espacio) -

1

Debe utilizar funciones en línea. Lea esto Inline Functions para una mejor comprensión y las compensaciones involucradas.

+0

El enlace ya no es válido –

+0

Esto realmente no se aplica inmediatamente a la pregunta, la pregunta no es "¿Cómo puedo mejorar el rendimiento?" o tal. Más apropiado sería decir que se llama una "función en línea" y dar un enlace explicando las ramificaciones de los mismos. – Kit10

12

Dependiendo de su compilador y su configuración se pueden realizar cualquiera de las siguientes:

  • Se puede ignorar la palabra clave en línea (que es sólo una sugerencia para el compilador, no un comando ) y generar soporte -alone funciones. Puede hacer esto si sus funciones exceden un umbral de complejidad dependiente del compilador. p.ej. demasiados bucles anidados.
  • Puede decidir que su función independiente es un buen candidato para la expansión en línea .

En muchos casos, el compilador se encuentra en una posición mucho mejor para determinar si una función debe estar dentro de la que está, por lo que no tiene sentido cuestionarla. Me gusta usar la inclusión implícita cuando una clase tiene muchas funciones pequeñas solo porque es conveniente tener la implementación allí mismo en la clase. Esto no funciona tan bien para funciones más grandes.

La otra cosa a tener en cuenta es que si está exportando una clase en una biblioteca DLL/compartida (no es una buena idea en mi humilde opinión, pero la gente lo hace de todos modos) debe ser muy cuidadoso con las funciones en línea. Si el compilador que construyó el DLL decide una función debe ser inline usted tiene un par de problemas potenciales:

  1. El compilador construir el programa usando la DLL podría decidir no inline la función por lo que generar una referencia de símbolo a una función que no existe y la DLL no se cargará.
  2. Si actualiza el archivo DLL y cambiar el función inline, el programa cliente todavía será utilizando la vieja versión de esa función ya que la función obtuve entre líneas en el código de cliente.
+0

Agradable respuesta. Gracias :) Por cierto, mis funciones son virtuales y ¿habrá alguna diferencia cuando esté en línea? –

+2

Las funciones virtuales no pueden estar en línea, deben ser referenciadas a través de un puntero en el vtable. Nunca lo intenté, pero el compilador debería ignorar el en línea o quejarse. – Ferruccio

+1

Las funciones virtuales pueden incluirse si el tipo se conoce en tiempo de compilación. Eso es muy raro en la práctica. – Tom

1

C++ no se quejará si lo hace, pero en general, no debería.

cuando #incluye un archivo, todo el contenido del archivo incluido se inserta en el punto de inclusión. Esto significa que cualquier definición que ponga en su encabezado se copiará en cada archivo que incluya ese encabezado.

Para proyectos pequeños, esto no es probable que sea un gran problema. Pero para proyectos más grandes, esto puede hacer que las cosas tarden más tiempo en compilarse (ya que el mismo código se vuelve a compilar cada vez que se encuentra) y podría aumentar considerablemente el tamaño del ejecutable. Si realiza un cambio en una definición en un archivo de código, solo ese archivo .cpp necesita ser recompilado. Si realiza un cambio en una definición en un archivo de encabezado, cada archivo de código que incluya el encabezado debe ser recompilado. ¡Un pequeño cambio puede hacer que tengas que recompilar todo tu proyecto!

A veces se hacen excepciones para funciones triviales que es poco probable que cambien (por ejemplo, cuando la definición de la función es una línea).

Cuestiones relacionadas