Pergunta

Eu sou um moderadamente experiente Java / programador C #, e eu tenho começado recentemente a aprender C ++. O problema é que eu estou tendo dificuldade para entender como estruturar os vários arquivos de cabeçalho e código. Este parece principalmente devido a minha falta de compreensão de como a ligações compilador tudo juntos. Eu tentei ler alguns livros, mas meus preconceitos são fortemente colorida por meu # conhecimento Java e C. Por exemplo, eu estou tendo dificuldade em se confrontar com o fato de que os métodos e similares podem ser definidos em um namespace em vez de apenas em uma definição de classe.

Eu encontrei uma abundância de C ++ - Guias> Java / C #, mas praticamente nada para ir por outro caminho. Existem bons recursos lá fora para aliviar o Java / C # -> C ++ transição, particularmente com respeito à compreensão do processo de compilação

Foi útil?

Solução

O C ++ FAQ é um excelente recurso sobre todas as idiossincrasias C ++, mas é provavelmente um pouco mais avançado do que você está procurando -. a maioria das perguntas (e não apenas as respostas) são mistérios, mesmo para os desenvolvedores bastante experientes C ++

Eu acho que se você google para tutoriais C ++, você vai ser capaz de encontrar algo. Você também pode querer tentar aprender linguagem de montagem (ou pelo menos obter uma introdução rápida de como as coisas realmente acontecer em um microprocessador), como C e C ++ são bastante próximo do hardware na maneira de fazer as coisas. Este é o lugar onde a sua velocidade e poder vem, mas ele vem com o preço de alguns dos mais agradáveis ??abstrações ofertas Java.

Eu posso tentar responder às suas perguntas específicas acima, mas eu não sei o quão bem eu vou fazer.

Uma das chaves para a compreensão da relação entre arquivos de cabeçalho e arquivos cpp é entender a idéia de uma "unidade de tradução". Um arquivo de classe Java pode ser considerado uma unidade de tradução, uma vez que é a unidade básica que é compilado em um formato binário. Em C ++, praticamente todos os arquivos cpp é uma unidade de tradução (há exceções, se você está fazendo coisas estranhas).

Um arquivo de cabeçalho pode ser incluído em várias unidades de tradução (e deve ser incluído em todos os lugares que usa o que estiver definido no cabeçalho). A directiva #include literalmente apenas faz uma substituição de texto - o conteúdo do arquivo incluído são inseridos na íntegra quando a directiva #include é. Você normalmente deseja que o seu interface de classe a ser definido no arquivo de cabeçalho, ea implementação no arquivo cpp. Isso é porque você não quer ser expor seus detalhes de implementação para outras unidades de tradução que podem incluir o cabeçalho. Em C ++, tudo, incluindo as classes, não são realmente objetos ricos, mas pedaços apenas de memória que os cessionários compilador significado para ... compilando a mesma informação do cabeçalho em cada unidade de tradução, as garantias do compilador que todas as unidades de tradução têm a mesmo entendimento do que um pedaço de memória representa. Devido à falta de dados ricos após a compilação, coisas como reflexão são impossíveis.

O segundo passo no processo de construção de C ++ está ligando, que é onde o ligante leva todas as unidades de tradução compilados e procura por símbolos (geralmente chamadas de função, mas também variáveis) utilizados em uma unidade de tradução, mas não ali definidas. Ele então olha para outra unidade de tradução que define esse símbolo e "links"-los juntos, de modo que todas as chamadas para uma função específica são direcionados para a unidade de tradução que o define.

No caso de métodos de classe, eles devem ser chamados através de uma instância de classe, que está por trás das cenas apenas um ponteiro para um pedaço de memória. Quando o compilador vê esses tipos de chamadas de método, ele gera um código que chama uma função, passando implicitamente o ponteiro, conhecido como o ponteiro this, para a função como o primeiro argumento. Você pode ter funções que não pertencem às classes (não métodos, como você disse, porque um método é propriamente uma função membro de uma classe e, portanto, não pode existir sem uma classe) porque o vinculador não tem o conceito de uma classe. Ele vai ver uma unidade de tradução que define uma função e outra que chama uma função e amarrá-los juntos.

Isso acabou sendo muito mais tempo do que eu esperava, e, claro, é uma simplificação exagerada, mas é preciso para o melhor de meu conhecimento e o nível de detalhe fornecido ... espero que ajude alguns. Pelo menos deve dar-lhe um ponto de partida para algum googling.

Outras dicas

Isso é algo que me confundiu quando eu comecei a usar C também. Livros não fazer um bom trabalho de descrever o uso correto dos cabeçalhos vs. arquivos de código.

O compilador funciona através do carregamento de cada arquivo .cpp e compilá-lo independente de todos os outros. O primeiro passo na compilação é carregar todos os cabeçalhos referidos por instruções # include. Você pode pensar nisso fazendo uma inserção textual de toda a foo.h onde quer que haja um #include "foo.h".

Quais são as implicações disso para a forma de estruturar seus arquivos? arquivos de cabeçalho deve ter o que partes do programa são necessários para outros arquivos .cpp para se referir a. Como regra geral, implementações não deve estar em arquivos de cabeçalho. Isso causará problemas. arquivos de cabeçalho deve incluir declarações de classes, funções e variáveis ??globais (se você deve usá-los).

Eu realmente recomendo ficar longe de explicações sobre compiladores C ++ e olhando para explicações em compiladores C. Na minha experiência, estes são explicados melhor e evitar confundir-lo com questões OOP. Olhe para o material sobre C compilação separada. Eu teria chamado você um grande livreto slide da minha alma mater, mas não é em Inglês.

A principal diferença entre a compilação C e Java / C # é que a compilação não cria uma entidade resolvido. Em outras palavras, quando você compilar em Java, os olhares do compilador para arquivos de classe já compilados para as classes referenciadas, e garante que tudo está disponível e consistente. O pressuposto subjacente é que, quando você eventualmente executar o programa, esses arquivos também estaria disponível.

Um arquivo compilado C, por outro lado, é apenas uma "promessa". Ele se baseia em uma declaração de que as dependências seria semelhante (na forma de declarações de função), mas não há garantias de que estes são definidos em qualquer lugar. O interruptor mais mentalidade difícil que você precisa fazer é pensar em um arquivo C não apenas como esse arquivo, mas sim como a agregação de que arquivo com tudo o que ele inclui (ou seja, o que o pré-processador gera). Em outras palavras, o compilador não vê arquivos de cabeçalho, parece um arquivo grande. O compilador se mantém no arquivo objeto gerado de tudo o que "ainda está faltando". Mais tarde, em tempo de link, o vinculador faz liquida isso tentando preencher todos os espaços em branco com materiais de diferentes arquivos objeto.

Você pode querer saber porque a compilação e vinculação são separados bem (desde que eu não ver nenhuma mensagem explicando-lo, e é a causa de muita confusão sem saber as razões subjacentes de coisas).

Linking e compilação é concluída separadamente porque (e pode haver mais de uma razão) da necessidade de fazer chamadas de biblioteca. se você definido ou qualquer um de sua laia, o código de implementação dos protótipos de função nesses cabeçalhos são parte do libary que já é compilado e sentado como um lugar de código objeto. se um processo de compilação gigante estavam a ser utilizados em vez disso, você precisa ter a fonte para as chamadas de biblioteca, bem como mais tempo durante a compilação porque você estaria também compilar o código da biblioteca.

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