Pergunta

Muitas línguas, tais como Java, C #, não fazer declaração separada de implementação. C # tem um conceito de classe parcial, mas a implementação e declaração ainda permanecem no mesmo arquivo.

Por que não C ++ têm o mesmo modelo? É mais prático ter arquivos de cabeçalho?

Estou me referindo às versões atuais e futuras do padrão C ++.

Foi útil?

Solução

Eu rotineiramente virar entre C # e C ++, e a falta de arquivos de cabeçalho em C # é uma das minhas maiores irrita. Eu posso olhar para um arquivo de cabeçalho e aprender tudo necessidade I saber sobre uma classe - o que é membro funções são chamados, sua sintaxe de chamada, etc - sem ter que percorrer páginas do código que implementa a classe

.

E sim, eu sei sobre classes parciais e #regions, mas não é o mesmo. classes parciais realmente piorar o problema, porque uma definição de classe está espalhada por vários arquivos. Tanto quanto #regions ir, eles nunca parecem ser expandido da maneira que eu gostaria para o que estou fazendo no momento, então eu tenho que gastar tempo expandindo os pouco mais é até que eu obter o direito de vista.

Talvez se intellisense do Visual Studio trabalhou melhor para C ++, eu não teria uma razão para ter de consultar arquivos .h tantas vezes, mas mesmo em VS2008, C ++ 'intellisense s não pode tocar C #' s

Outras dicas

Compatibilidade com versões anteriores -. Arquivos de cabeçalho não são eliminados porque iria quebrar compatibilidade com versões anteriores

Os arquivos de cabeçalho permitir a compilação independente. Você não precisa de acesso ou até mesmo ter os arquivos de implementação para compilar um arquivo. Isso pode fazer para facilitar a distribuição constrói.

Isso também permite que SDKs a ser feito um pouco mais fácil. Você pode fornecer apenas os cabeçalhos e algumas bibliotecas. Há, é claro, maneiras de contornar isso, que outras línguas usam.

Mesmo Bjarne Stroustrup chamou arquivos de cabeçalho um remendo.

Mas, sem um formato binário padrão que inclui os metadados necessários (como arquivos de classe Java ou arquivos .Net PE) Eu não vejo nenhuma maneira de implementar o recurso. A despojado ELF ou binário a.out não tem muita da informação que você precisa para extrair. E eu não acho que a informação é sempre armazenados em arquivos do Windows XCOFF.

Na The Design e Evolução da C ++, Stroustrup dá mais uma razão ...

O arquivo cabeçalho pode ter dois ou mais arquivos de implementação que podem ser trabalhados-upon simultaneamente por mais de um programador, sem a necessidade de um sistema de controle de origem.

Isso pode parecer estranho nos dias de hoje, mas eu acho que foi uma questão importante quando C ++ foi inventada.

C foi feito para tornar a escrita um compilador facilmente. Ele faz um monte de coisas com base em que um princípio. Ponteiros só existem para tornar a escrita um compilador mais fácil, assim como os arquivos de cabeçalho. Muitas das coisas que transita para C ++ são baseados em compatibilidade com estas características implementadas para tornar compilador escrita mais fácil.

É uma boa ideia, na verdade. Quando C foi criado, C e Unix foram tipo de um par. C portado Unix, Unix correu C. Desta forma, C e Unix poderia se espalhar rapidamente de plataforma em plataforma enquanto um sistema operacional baseado em montagem teve que ser completamente re-escrito para ser portado.

O conceito de especificar uma interface em um arquivo e a implementação em outro não é uma má ideia de todo, mas não é isso que arquivos de cabeçalho C estão. Eles são simplesmente uma maneira de limitar o número de passa um compilador tem que fazer através de seu código-fonte e permitir alguma abstração limitada do contrato entre arquivos para que eles possam se comunicar.

Esses itens, ponteiros, arquivos de cabeçalho, etc ... realmente não oferecem qualquer vantagem sobre outro sistema. Colocando mais esforço para o compilador, você pode compilar um objeto de referência tão facilmente como um ponteiro para exatamente o mesmo código de objeto. Isto é o que C ++ faz agora.

C é uma grande linguagem, simples. Tinha um conjunto de recursos muito limitados, e você poderia escrever um compilador sem muito esforço. Portando é geralmente trivial! Eu não estou tentando dizer que é uma má língua ou qualquer outra coisa, é só que os objetivos principais da C quando foi criado pode deixar restos na língua que são mais ou menos desnecessária agora, mas vão ser mantido em torno de compatibilidade.


Parece que algumas pessoas realmente não acredito que C foi escrito ao porto Unix, por isso aqui: ( a partir )

A primeira versão do UNIX foi escrito em linguagem assembler, mas Thompson intenção era que ele seria escrito em uma linguagem de alto nível.

Thompson tentou pela primeira vez em 1971 para usar Fortran no PDP-7, mas desistiu após o primeiro dia. Então ele escreveu um muito linguagem simples que chamou de B, que ele tem em curso o PDP-7. isto funcionou, mas houve problemas. Primeiro, porque a implementação foi interpretados, foi sempre vai ser lento. Em segundo lugar, as noções básicas de B, que foi baseado no orientada palavra- BCPL, só não estava certo para um máquina orientada bytes como o novo PDP-11.

Ritchie usou o PDP-11 para adicionar tipos em B, o que durante algum tempo foi chamado NB para "New B", e então ele começou a escrever um compilador para ele. "Assim que o primeira fase C foi realmente estes dois fases em curto sucessão de, em primeiro lugar, algumas mudanças na linguagem de B, realmente, adicionando a estrutura do tipo sem demasiado muita mudança na sintaxe; e fazendo o compilador ", disse Ritchie.

"A segunda fase foi mais lento", disse ele de reescrever UNIX em C. Thompson começou no verão de 1972, mas teve dois problemas: descobrir como funcionam os co-rotinas básicas, ou seja, como de controlo de comutação de um processo de outro; ea dificuldade na obtenção de a estrutura de dados adequada, uma vez que o versão original do C não têm estruturas.

"A combinação das coisas que causou Ken a desistir durante o verão," disse Ritchie. "Durante o ano, eu adicionei estruturas e, provavelmente, fez a código de compilador um pouco melhor - um código melhor - e assim durante a próxima verão, que foi quando fizemos a concertada esforço e realmente fez redo todo o sistema operacional em C ".


Aqui está um exemplo perfeito do que quero dizer. Dos comentários:

Os ponteiros só existem para tornar a escrita um compilador mais fácil? existem ponteiros N.º porque eles são o mais simples abstração possível sobre a idéia de engano. - Adam Rosenfield (uma hora)

Você tem razão. A fim de implementar indirection, os ponteiros são os mais simples abstração possível implementar. De nenhuma maneira eles são o mais simples possível compreender ou usar. Matrizes são muito mais fácil.

O problema? Para implementar matrizes de forma tão eficiente como ponteiros que você tem que adicionar praticamente uma pilha enorme de código para o seu compilador.

Não há nenhuma razão eles não poderiam ter projetado C sem ponteiros, mas com um código como este:

int i=0;
while(src[++i])
    dest[i]=src[i];

vai demorar muito esforço (por parte compiladores) para fator o i explícita + src e I + adições dest e torná-lo criar o mesmo código que isso faria:

while(*(dest++) = *(src++))
    ;

Factoring que variável "i" após o fato é difícil. Novos compiladores pode fazê-lo, mas naquela época ele simplesmente não era possível, e o sistema operacional em execução no hardware de baixa qualidade necessária pequenas otimizações assim.

Agora, alguns sistemas precisa desse tipo de otimização (eu trabalho em uma das plataformas mais lentos ao redor - set-top boxes de cabo, ea maioria do nosso material é em Java) e, no caso raro em que você pode precisar dele, o novos compiladores C deve ser inteligente o suficiente para fazer esse tipo de conversão por conta própria.

Se você quer C ++, sem arquivos de cabeçalho, em seguida, eu tenho boas notícias para você.

Ela já existe e é chamado de D ( http://www.digitalmars.com/d/ index.html )

Tecnicamente D parece ser um mais agradável monte de C ++, mas ele simplesmente não é suficiente mainstream para uso em diversas aplicações no momento.

Um dos C ++ 's objetivos é ser um super conjunto de C, e é difícil para ele a fazê-lo se não pode suportar arquivos de cabeçalho. E, por extensão, se você deseja arquivos de cabeçalho especiais de consumo assim como você pode considerar excisão CPP (pré-processador, não plus-plus) completamente; C # e Java não especificar macro pré-processadores com seus padrões (mas deve-se notar, em alguns casos eles podem ser e ainda são usados ??mesmo com línguas).

Como C ++ é projetado agora, você precisa de protótipos - como em C - para verificar estaticamente qualquer código compilado, que referencia funções externas e classes. Sem arquivos de cabeçalho, você teria que digitar essas definições de classe e declarações de função antes de usá-los. Para C ++ não usar arquivos de cabeçalho, você teria que adicionar um recurso na linguagem que apoiaria algo como palavra-chave import de Java. Isso seria uma grande adição e mudança; responder à sua pergunta de se seria prático: Eu não penso assim - não em todos

.

Muitas pessoas estão conscientes das deficiências de arquivos de cabeçalho e há ideias para introduzir mais poderoso sistema de módulos para C ++. Você pode querer dar uma olhada módulos em C ++ (Revisão 5) por Daveed Vandevoorde.

Bem, C ++, por si só não deve eliminar arquivos de cabeçalho por causa de compatibilidade com versões anteriores. No entanto, eu acho que eles são uma idéia idiota em geral. Se você quiser distribuir um lib-fonte fechado, esta informação pode ser extraída automaticamente. Se você quer entender como usar uma classe w / o olhar para a implementação, é isso que os geradores de documentação são para, e eles fazem um pedaço de um muito melhor um emprego.

Há um valor para definir a interface de classe em um componente separado para o arquivo de implementação.

Pode ser feito com interfaces, mas se você ir por esse caminho, então você está dizendo implicitamente que as aulas são deficientes em termos de separar a implementação de contrato.

Modula 2 teve a idéia certa, módulos de definição e módulos de implementação. http://www.modula2.org/reference/modules.php

Java / C # 's resposta é uma implementação implícita do mesmo (embora orientada a objetos).

arquivos

Header é um truque, porque os arquivos de cabeçalho expressar detalhe de implementação (tais como variáveis ??privadas.)

Na passagem para Java e C #, acho que, se uma língua requer suporte IDE para o desenvolvimento (tal que as interfaces de classe públicas são navegáveis ??em navegadores de classes), então este é talvez uma declaração de que o código não estar no seu méritos próprios como sendo particularmente legível.

I encontrar a combinação de interfaces com detalhe de implementação bastante horrendo.

Essencialmente, a falta de capacidade de documentar a assinatura de classe pública em um conciso bem comentado independente da implementação do arquivo indica-me que o design linguagem é escrito por conveniência da autoria, em vez conveniência de manutenção. Bem, eu estou divagar sobre Java e C # agora.

Uma vantagem desta separação é que é fácil de ver somente a interface, sem requer um editor avançado .

Se você quiser a razão por que isso nunca vai acontecer: ele iria quebrar praticamente todo o software existente C ++. Se você olhar para alguns dos ++ documentação de projeto comitê C, eles olharam para várias alternativas para ver a quantidade de código que iria quebrar.

Seria muito mais fácil mudar a instrução switch em algo no meio do caminho inteligente. Isso quebraria apenas um pouco de código. Ele ainda não vai acontecer.

editado para NEW IDEA:

A diferença entre C ++ e Java que faz C ++ arquivos de cabeçalho necessários é que os objetos C ++ não são necessariamente ponteiros. Em Java, todas as instâncias de classe são referidos por ponteiro, apesar de não parecer assim. C ++ tem objetos alocados na pilha ea pilha. Este meio C ++ precisa de uma forma de saber quão grande um objeto será, e onde os membros de dados estão na memória.

Nenhuma linguagem existe sem arquivos de cabeçalho. É um mito.

Olhe para qualquer distribuição biblioteca proprietária para Java (não tenho C # experiência para falar, mas eu esperaria que é o mesmo). Eles não dão a você o arquivo-fonte completo; eles apenas dar-lhe um arquivo com a implementação de cada método blanked ({} ou {return null;} ou semelhantes) e tudo o que eles podem fugir com esconder escondido. Você não pode chamar isso de qualquer coisa, mas um cabeçalho.

Não há nenhuma razão técnica, no entanto, por que compilador C ou C ++ poderia contar tudo em um arquivo apropriadamente marcado como extern a menos que o arquivo está sendo compilado diretamente. No entanto, os custos para a compilação seria imensa porque nem C nem C ++ é rápido para analisar, e isso é uma consideração muito importante. Qualquer método mais complexo de fusão cabeçalhos e fonte de rapidamente encontrar problemas técnicos, como a necessidade de que o compilador sabe o layout de um objeto.

arquivos

Header são parte integrante da língua. Sem arquivos de cabeçalho, todas as bibliotecas estáticas, bibliotecas dinâmicas, biblioteca praticamente qualquer pré-compilado torna-se inútil. Arquivos de cabeçalho também torná-lo mais fácil de documento tudo, e torná-lo possível para olhar sobre API de uma biblioteca / arquivo sem passar cada pedaço de código.

Eles também torná-lo mais fácil de organizar o seu programa. Sim, você tem que ser constantemente mudar de fonte de cabeçalho, mas eles também permitem que você definir APIs internas e privadas dentro das implementações. Por exemplo:

MySource.h:

extern int my_library_entry_point(int api_to_use, ...);

MySource.c:

int private_function_that_CANNOT_be_public();

int my_library_entry_point(int api_to_use, ...){
  // [...] Do stuff
}

int private_function_that_CANNOT_be_public() {

}

Se você #include <MySource.h>, então você obtém my_library_entry_point.

Se você #include <MySource.c>, então você também obter private_function_that_CANNOT_be_public.

Você vê como isso poderia ser uma coisa muito ruim se você tivesse uma função para obter uma lista de senhas, ou uma função que implementou o algoritmo de criptografia, ou uma função que iria expor o funcionamento interno de um sistema operacional, ou uma função que privilégios cancelou, etc.

Oh sim!

Depois de codificação em Java e C # é realmente chato ter 2 arquivos para cada classes. Então eu estava pensando como eu posso fundi-los sem quebrar o código existente.

Na verdade, é realmente fácil. Basta colocar a definição (implementação) dentro de uma seção #ifdef e adicionar uma definição na linha de comando do compilador para compilar esse arquivo. É isso.

Aqui está um exemplo:

/* File ClassA.cpp */

#ifndef _ClassA_
#define _ClassA_

#include "ClassB.cpp"
#include "InterfaceC.cpp"

class ClassA : public InterfaceC
{
public:
    ClassA(void);
    virtual ~ClassA(void);

    virtual void methodC();

private:
    ClassB b;
};

#endif

#ifdef compiling_ClassA

ClassA::ClassA(void)
{
}

ClassA::~ClassA(void)
{
}

void ClassA::methodC()
{
}

#endif

Na linha de comando, compilar esse arquivo com

-D compiling_ClassA

Os outros arquivos que necessidade de incluir ClassA pode apenas fazer

#include "ClassA.cpp"

É claro que a adição da definição na linha de comando podem ser facilmente adicionados com uma expansão de macro (compilador Visual Studio) ou com um variáveis ??automáticas (GNU make) e usando a mesma nomenclatura para o nome definido.

Ainda Eu não entendo o ponto de algumas declarações. Separação de API e implementação é uma coisa muito boa, mas cabeçalho arquivos não são API. Existem campos particulares lá. Se você adicionar ou remover campo privado de alterar a implementação e não API.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top