En C, no puede tener la definición / implementación de la función dentro del archivo de encabezado. Sin embargo, en C ++ puede tener una implementación completa del método dentro del archivo de encabezado. ¿Por qué es diferente el comportamiento?
En C, si define una función en un archivo de encabezado, esa función aparecerá en cada módulo que se compila que incluya ese archivo de encabezado, y se exportará un símbolo público para la función. Entonces, si la función additup se define en header.h, y foo.c y bar.c ambos incluyen header.h, entonces foo.o y bar.o incluirán copias de additup.
Cuando vaya a vincular esos dos archivos de objetos, el vinculador verá que el complemento de símbolo se define más de una vez y no lo permitirá.
Si declara que la función es estática, no se exportará ningún símbolo. Los archivos de objeto foo.o y bar.o seguirán conteniendo copias separadas del código para la función, y podrán usarlos, pero el enlazador no podrá ver ninguna copia de la función, por lo que No me quejaré. Por supuesto, ningún otro módulo podrá ver la función tampoco. Y su programa se hinchará con dos copias idénticas de la misma función.
Si solo declara la función en el archivo de encabezado, pero no la define, y luego la define en un solo módulo, el vinculador verá una copia de la función, y cada módulo en su programa podrá verla y úsalo. Y su programa compilado contendrá solo una copia de la función.
Por lo tanto, puede tener la definición de la función en el archivo de encabezado en C, es simplemente un mal estilo, una forma incorrecta y una mala idea general.
(Por "declarar", me refiero a proporcionar un prototipo de función sin cuerpo; por "definir" me refiero a proporcionar el código real del cuerpo de la función; esta es la terminología estándar de C.)
#ifndef HEADER_H
se supone que debe prevenir?C y C ++ se comportan de la misma manera en este aspecto: puede tener
inline
funciones en los encabezados. En C ++, cualquier método cuyo cuerpo está dentro de la definición de clase está implícitamenteinline
. Si desea hacer lo mismo en C, declare las funcionesstatic inline
.fuente
static inline
" ... y aún tendrás múltiples copias de la función en cada unidad de traducción que la use. En C ++ sinstatic
inline
función, solo tendrá una copia. Para tener la implementación en el encabezado en C, debe 1) marcar la implementación comoinline
(por ejemploinline void func(){do_something();}
) y 2) decir que esta función estará en alguna unidad de traducción en particular (por ejemplovoid func();
).El concepto de un archivo de encabezado necesita una pequeña explicación:
O le das un archivo en la línea de comando del compilador, o haces un '#include'. La mayoría de los compiladores aceptan un archivo de comando con la extensión c, C, cpp, c ++, etc. como el archivo fuente. Sin embargo, generalmente incluyen una opción de línea de comandos para permitir el uso de cualquier extensión arbitraria a un archivo fuente también.
En general, el archivo dado en la línea de comando se llama 'Fuente', y el que se incluye se llama 'Encabezado'.
El paso del preprocesador en realidad los toma a todos y hace que todo aparezca como un solo archivo grande para el compilador. Lo que estaba en el encabezado o en la fuente en realidad no es relevante en este momento. Generalmente hay una opción de un compilador que puede mostrar el resultado de esta etapa.
Entonces, para cada archivo que se proporcionó en la línea de comandos del compilador, se le entrega un archivo enorme al compilador. Esto puede tener código / datos que ocuparán memoria y / o crearán un símbolo para ser referenciado desde otros archivos. Ahora cada uno de estos generará una imagen de 'objeto'. El enlazador puede dar un 'símbolo duplicado' si el mismo símbolo se encuentra en más de dos archivos de objetos que se están vinculando entre sí. Quizás esta sea la razón; No se recomienda poner código en un archivo de encabezado, que puede crear símbolos en el archivo objeto.
Los 'en línea' generalmente están en línea ... pero al depurar pueden no estar en línea. Entonces, ¿por qué el enlazador no da errores definidos multiplicados? Simple ... Estos son símbolos 'débiles', y siempre y cuando todos los datos / códigos para un símbolo débil de todos los objetos tengan el mismo tamaño y contenido, el enlace mantendrá una copia y soltará una copia de otros objetos. Funciona.
fuente
Puede hacer esto en C99:
inline
se garantiza que las funciones se proporcionarán en otro lugar, por lo que si una función no estaba en línea, su definición se traduce en una declaración (es decir, la implementación se descarta). Y, por supuesto, puedes usarstatic
.fuente
Cotizaciones estándar de C ++
El borrador estándar de C ++ 17 N4659 10.1.6 "El especificador en línea" dice que los métodos están implícitamente en línea:
y luego más abajo vemos que los métodos en línea no solo pueden, sino que deben definirse en todas las unidades de traducción:
Esto también se menciona explícitamente en una nota en 12.2.1 "Funciones de miembro":
Implementación de GCC 8.3
main.cpp
Compilar y ver símbolos:
salida:
entonces vemos
man nm
que elMyClass::myMethod
símbolo está marcado como débil en los archivos de objetos ELF, lo que implica que puede aparecer en múltiples archivos de objetos:fuente
Probablemente por la misma razón por la que debe poner la implementación del método completo dentro de la definición de clase en Java.
Pueden parecer similares, con corchetes y muchas de las mismas palabras clave, pero son idiomas diferentes.
fuente