Acho que os arquivos de cabeçalho são úteis ao navegar nos arquivos de origem C ++, porque eles fornecem um "resumo" de todas as funções e membros de dados de uma classe. Por que tantas outras linguagens (como Ruby, Python, Java etc.) não possuem um recurso como este? Essa é uma área em que a verbosidade do C ++ é útil?
20
Respostas:
O objetivo original dos arquivos de cabeçalho era permitir a compilação e a modularidade de passagem única em C. Ao declarar os métodos antes de serem usados, permitia apenas a passagem de compilação única. Essa era já se foi há muito tempo, graças aos nossos computadores poderosos serem capazes de fazer compilação de várias passagens sem problemas e, às vezes, até mais rápido que os compiladores C ++.
O C ++, sendo retrocompatível com o C, precisava manter os arquivos de cabeçalho, mas adicionou muitos deles, o que resultou em um design bastante problemático. Mais em FQA .
Para modularidade, os arquivos de cabeçalho eram necessários como metadados sobre o código nos módulos. Por exemplo. quais métodos (e nas classes C ++) estão disponíveis em qual biblioteca. Era óbvio que o desenvolvedor escrevesse isso, porque o tempo de compilação era caro. Atualmente, não há problema em o compilador gerar esses metadados a partir do próprio código. As linguagens Java e .NET fazem isso normalmente.
Então não. Arquivos de cabeçalho não são bons. Eles eram quando ainda tínhamos que ter o compilador e o vinculador em disquetes separados e a compilação levou 30 minutos. Hoje em dia, eles apenas atrapalham e são sinal de mau design.
fonte
Embora possam ser úteis para você como uma forma de documentação, o sistema em torno dos arquivos de cabeçalho é extraordinariamente ineficiente.
C foi projetado para que cada passo de compilação construa um único módulo; cada arquivo de origem é compilado em uma execução separada do compilador. Os arquivos de cabeçalho, por outro lado, são injetados nessa etapa de compilação para cada um dos arquivos de origem que os referenciam.
Isso significa que, se o seu arquivo de cabeçalho for incluído em 300 arquivos de origem, ele será analisado e compilado repetidamente, 300 vezes em separado enquanto o programa é criado. Exatamente a mesma coisa com o mesmo resultado, repetidamente. Isso é uma enorme perda de tempo e é uma das principais razões pelas quais os programas C e C ++ demoram tanto para serem criados.
Todas as línguas modernas evitam intencionalmente essa absurda pouca ineficiência. Em vez disso, normalmente em linguagens compiladas, os metadados necessários são armazenados na saída da compilação, permitindo que o arquivo compilado atue como uma espécie de referência de pesquisa rápida que descreve o conteúdo do arquivo compilado. Todos os benefícios de um arquivo de cabeçalho, criado automaticamente sem nenhum trabalho adicional de sua parte.
Como alternativa em idiomas interpretados, todo módulo carregado é mantido na memória. Fazer referência ou incluir ou exigir alguma biblioteca lerá e compilará o código-fonte associado, que permanece residente até o término do programa. Se você também precisar dele em outro lugar, não haverá trabalho adicional, pois já foi carregado.
Em ambos os casos, você pode "navegar" pelos dados criados por esta etapa usando as ferramentas do idioma. Normalmente, o IDE terá algum tipo de navegador de classe. E se o idioma tiver um REPL, também poderá ser usado frequentemente para gerar um resumo da documentação de qualquer objeto carregado.
fonte
Não está claro o que você quer dizer com navegando no arquivo por funções e membros de dados. No entanto, a maioria dos IDEs fornece ferramentas para navegar na classe e ver membros de dados da classe.
Por exemplo: O Visual Studio possui
Class View
eObject browser
que fornece muito bem a funcionalidade solicitada. Como nas seguintes capturas de tela.fonte
Uma desvantagem adicional dos arquivos de cabeçalho é que o programa depende da ordem de inclusão e o programador precisa executar etapas extras para garantir a correção.
Isso, associado ao sistema macro de C (herdado de C ++), leva a muitas armadilhas e situações confusas.
Para ilustrar, se um cabeçalho definisse algum símbolo usando macros para seu uso, e outro cabeçalho usasse o símbolo de outra maneira, como o nome de uma função, a ordem de inclusão influenciaria bastante o resultado final. Há muitos exemplos assim.
fonte
Eu sempre gostei de arquivos de cabeçalho, pois eles fornecem uma forma de interface para a implementação, juntamente com algumas informações extras, como variáveis de classe, tudo em um arquivo fácil de visualizar.
Eu vejo muitos códigos C # (que não precisam de 2 arquivos por classe) escritos usando 2 arquivos por classe - a implementação da classe real e outra com uma interface. Esse design é bom para zombar (essencial em alguns sistemas) e ajuda a definir a documentação da classe sem precisar usar o IDE para exibir os metadados compilados. Eu iria tão longe para dizer suas boas práticas.
Portanto, o C / C ++ exigindo um equivalente (das sortes) de uma interface nos arquivos de cabeçalho é uma coisa boa.
Eu sei que existem defensores de outros sistemas que não gostam deles por razões que incluem 'é mais difícil hackear código se você precisar colocar 2 arquivos', mas minha atitude é que apenas hackear código não é uma boa prática. e, depois de começar a escrever / projetar código com um pouco mais de atenção, você definirá cabeçalhos / interfaces normalmente.
fonte
Na verdade, eu diria que os arquivos de cabeçalhos não são ótimos porque eles confundem a interface e a implementação. A intenção da programação em geral, e especialmente do OOP, é ter uma interface definida e ocultar os detalhes da implementação, mas um arquivo de cabeçalho C ++ mostra os métodos, a herança e os membros públicos (interface), bem como métodos privados e membros privados (alguma parte da implementação). Sem mencionar que, em alguns casos, você acaba inserindo código ou construtores no arquivo de cabeçalho, e algumas bibliotecas incluem código de modelo nos cabeçalhos, o que realmente combina implementação com interface.
A intenção original, acredito, era possibilitar que o código usasse outras bibliotecas, objetos etc. sem precisar importar todo o conteúdo do script. Tudo o que você precisa é do cabeçalho para compilar e vincular. Economiza tempo e alterna dessa maneira. Nesse caso, é uma ideia decente, mas é apenas uma maneira de resolver esses problemas.
Quanto à navegação na estrutura do programa, a maioria dos IDEs oferece essa capacidade, e existem muitas ferramentas que irão lançar as interfaces, fazer análise de código, descompilação, etc. para que você possa ver o que está acontecendo nos bastidores.
Por que outras línguas não implementam o mesmo recurso? Bem, porque outros idiomas vêm de outras pessoas, e esses designers / criadores têm uma visão diferente de como as coisas devem funcionar.
A melhor resposta é ficar com o que o trabalho que você precisa fazer e fazer você feliz.
fonte
Em muitas linguagens de programação, quando um programa é subdividido em várias unidades de compilação, o código em uma unidade só poderá usar as coisas definidas em outra se o compilador tiver algum meio de saber quais são essas coisas. Em vez de exigir que um compilador que processa uma unidade de compilação examine o texto inteiro de cada unidade de compilação que define qualquer coisa usada na unidade atual, é melhor que o compilador receba, enquanto processa cada unidade, o texto completo da unidade a ser compilada com um pouco de informação de todos os outros.
Em C, o programador é responsável por criar dois arquivos para cada unidade - um contendo informações que só serão necessárias ao compilar essa unidade e um contendo informações que também serão necessárias ao compilar outras unidades. Este é um design bastante desagradável, hacky, mas evita a necessidade de um padrão de linguagem especificar qualquer coisa sobre como os compiladores devem gerar e processar arquivos intermediários. Muitas outras linguagens usam uma abordagem diferente, na qual um compilador gerará a partir de cada arquivo de origem um arquivo intermediário que descreve tudo naquele arquivo de origem que deveria estar acessível ao código externo. Essa abordagem evita a necessidade de informações duplicadas em dois arquivos de origem, mas requer que uma plataforma de compilação tenha definido a semântica de arquivos de uma maneira que não é necessária para C.
fonte