Em C, você não pode ter a definição / implementação de função dentro do arquivo de cabeçalho. No entanto, em C ++, você pode ter a implementação completa do método dentro do arquivo de cabeçalho. Por que o comportamento é diferente?
Em C, se você definir uma função em um arquivo de cabeçalho, essa função aparecerá em cada módulo compilado que inclui esse arquivo de cabeçalho e um símbolo público será exportado para a função. Portanto, se a adição de função for definida no cabeçalho.h, e foo.c e bar.c incluirem o cabeçalho.h, então foo.o e bar.o incluirão cópias da adição.
Quando você vincula esses dois arquivos de objeto, o vinculador verá que o acréscimo de símbolo está definido mais de uma vez e não permitirá.
Se você declarar que a função é estática, nenhum símbolo será exportado. Os arquivos de objeto foo.o e bar.o ainda conterão cópias separadas do código para a função e poderão usá-los, mas o vinculador não poderá ver nenhuma cópia da função, portanto não vai reclamar. Obviamente, nenhum outro módulo poderá ver a função também. E seu programa ficará cheio de duas cópias idênticas da mesma função.
Se você declarar apenas a função no arquivo de cabeçalho, mas não a definir, e depois defini-la em apenas um módulo, o vinculador verá uma cópia da função e todos os módulos do seu programa poderão vê-lo e use-o. E seu programa compilado conterá apenas uma cópia da função.
Assim, você pode ter a definição de função no arquivo de cabeçalho em C, é apenas estilo ruim, forma incorreta e uma péssima idéia geral.
(Por "declarar", quero dizer fornecer um protótipo de função sem um corpo; por "definir" quero dizer fornecer o código real do corpo da função; essa é a terminologia C padrão.)
#ifndef HEADER_H
é o que deveria impedir?C e C ++ se comportam da mesma forma nesse aspecto - você pode ter
inline
funções nos cabeçalhos. No C ++, qualquer método cujo corpo esteja dentro da definição de classe é implicitamenteinline
. Se você deseja fazer o mesmo em C, declare as funçõesstatic inline
.fonte
static inline
" ... e você ainda terá várias cópias da função em cada unidade de tradução que a usa. No C ++ com não-static
inline
função, você terá apenas uma cópia. Para realmente ter a implementação no cabeçalho em C, você deve 1) marcar a implementação comoinline
(por exemploinline void func(){do_something();}
) e 2) realmente dizer que essa função estará em alguma unidade de tradução específica (por exemplovoid func();
).O conceito de um arquivo de cabeçalho precisa de uma pequena explicação:
Você fornece um arquivo na linha de comando do compilador ou faz um '#include'. A maioria dos compiladores aceita um arquivo de comando com a extensão c, C, cpp, c ++ etc. como arquivo de origem. No entanto, eles geralmente incluem uma opção de linha de comando para permitir o uso de qualquer extensão arbitrária em um arquivo de origem.
Geralmente, o arquivo fornecido na linha de comando é chamado de 'Origem' e o arquivo incluído é chamado de 'Cabeçalho'.
A etapa do pré-processador realmente leva todos eles e faz com que tudo pareça um único arquivo grande para o compilador. O que estava no cabeçalho ou na fonte não é realmente relevante neste momento. Geralmente, há uma opção de um compilador que pode mostrar a saída desse estágio.
Portanto, para cada arquivo fornecido na linha de comando do compilador, um arquivo enorme é fornecido ao compilador. Isso pode ter código / dados que ocuparão memória e / ou criarão um símbolo a ser referenciado a partir de outros arquivos. Agora, cada um deles irá gerar uma imagem de 'objeto'. O vinculador pode fornecer um 'símbolo duplicado' se o mesmo símbolo for encontrado em mais de dois arquivos de objetos que estão sendo vinculados. Talvez seja essa a razão; não é aconselhável colocar código em um arquivo de cabeçalho, o que pode criar símbolos no arquivo de objeto.
Os 'embutidos' geralmente são embutidos ... mas, ao depurar, eles podem não estar embutidos. Então, por que o vinculador não fornece erros multiplamente definidos? Simples ... Esses são símbolos 'fracos' e, desde que todos os dados / códigos de um símbolo fraco de todos os objetos tenham o mesmo tamanho e conteúdo, o vinculado manterá uma cópia e largará a cópia de outros objetos. Funciona.
fonte
Você pode fazer isso no C99:
inline
é garantido que as funções sejam fornecidas em outro lugar; portanto, se uma função não foi incorporada, sua definição é traduzida em uma declaração (ou seja, a implementação é descartada). E, claro, você pode usarstatic
.fonte
Citações padrão em C ++
O rascunho padrão do C ++ 17 N4659 10.1.6 "O especificador em linha" diz que os métodos estão implicitamente embutidos:
e mais adiante, vemos que os métodos em linha não apenas podem, mas devem ser definidos em todas as unidades de tradução:
Isso também é mencionado explicitamente em uma nota em 12.2.1 "Funções-membro":
Implementação do GCC 8.3
main.cpp
Compile e visualize símbolos:
saída:
então vemos
man nm
que oMyClass::myMethod
símbolo está marcado como fraco nos arquivos de objeto ELF, o que implica que ele pode aparecer em vários arquivos de objeto:fonte
Provavelmente pelo mesmo motivo que você deve colocar a implementação completa do método dentro da definição de classe em Java.
Eles podem parecer semelhantes, com colchetes irregulares e muitas das mesmas palavras-chave, mas são idiomas diferentes.
fonte