Arquivos de header do C ++, separação de código

Eu sou novo no C ++ e tive algumas questões gerais sobre a separação de código. Eu construí atualmente um pequeno aplicativo, tudo em um arquivo. O que eu quero fazer agora é converter isso em arquivos separados, de modo que eles contenham código semelhante ou algo assim. Minha verdadeira pergunta agora é: como sei separar as coisas? Qual é a margem invisível em que o código deve ser separado?

Além disso, qual é o ponto dos arquivos de header? É para encaminhar declarar methods e classs para que eu possa usá-los no meu código antes que eles sejam incluídos pelo vinculador durante a compilation?

Qualquer insight sobre methods ou práticas recomendadas seria ótimo, obrigado!

Arquivos de header devem conter declarações de class e function.

Arquivos de origem contêm definições de class e function.

É uma prática padrão (isto é, ler mais fácil) ter uma declaração por arquivo de header e uma definição por arquivo de origem, embora para objects pequenos (leia ajudantes mais simples) você as agrupe com objects mais substanciais relacionados.

Exemplo: Menu de aula

Menu.h: Contains the Menu declaration. Menu.cpp: Contains the Menu definition. 

A razão pela qual os arquivos de header contêm as declarações é para que você possa incluí-los a partir de vários arquivos de origem e, portanto, cada arquivo de origem tenha exatamente a mesma definição de cada class e function.

Considere assim:
Se você não tiver arquivos de header, precisará ter as definições de declaração de class e / ou function (sem) em todos os arquivos de origem, isso significa uma cópia da mesma declaração em todos os arquivos. Assim, se você modificar uma class, precisará fazer a mesma modificação em todos os arquivos. Pelo uso de um arquivo de header, você tem a declaração em um lugar e, portanto, apenas um object para modificar.

Primeiro, você não deve colocar nada em headers que não seja necessário para ser visível por qualquer outro arquivo, além daquele que precisa dele. Então, vamos definir algo que precisamos abaixo.

Unidade de Tradução

Uma Unidade de Tradução é o código atual sendo compilado, e todo o código incluído por ele, direta ou indiretamente. Uma unidade de tradução é convertida em um arquivo .o / .obj.

Programa

Isso é todos os seus arquivos .o / .obj unidos em um arquivo binário que pode ser executado para formar um processo.

Quais são os principais pontos de ter diferentes unidades de tradução?

  1. Reduza dependencies, para que, se você alterar um método de uma class, não precise recompilar todo o código do programa, mas apenas a unidade de tradução afetada. A
  2. Reduza possíveis conflitos de nomes com nomes locais de unidade de tradução, que não são visíveis por outra unidade de tradução ao vinculá-los.

Agora, como você pode dividir seu código em diferentes unidades de tradução? A resposta é que não há “assim que você faz!”, Mas você tem que considerá-lo caso a caso. Muitas vezes é claro, já que você tem turmas diferentes, que podem e devem ser colocadas em diferentes unidades de tradução:

foo.hpp:

 /* Only declaration of class foo we define below. Note that a declaration * is not a definition. But a definition is always also a declaration */ class foo; /* definition of a class foo. the same class definition can appear in multiple translation units provided that each definition is the same basicially, but only once per translation unit. This too is called the "One Definition Rule" (ODR). */ class foo { /* declaration of a member function doit */ void doit(); /* definition of an data-member age */ int age; }; 

Declare algumas funções e objects livres:

 /* if you have translation unit non-local (with so-called extern linkage) names, you declare them here, so other translation units can include your file "foo.hpp" and use them. */ void getTheAnswer(); /* to avoid that the following is a definition of a object, you put "extern" in front of it. */ extern int answerCheat; 

foo.cpp:

 /* include the header of it */ #include "foo.hpp" /* definition of the member function doit */ void foo::doit() { /* ... */ } /* definition of a translation unit local name. preferred way in c++. */ namespace { void help() { /* ... */ } } void getTheAnswer() { /* let's call our helper function */ help(); /* ... */ } /* define answerCheat. non-const objects are translation unit nonlocal by default */ int answerCheat = 42; 

bar.hpp:

 /* so, this is the same as above, just with other classs/files... */ class bar { public: bar(); /* constructor */ }; 

bar.cpp:

 /* we need the foo.hpp file, which declares getTheAnswer() */ #include "foo.hpp" #include "bar.hpp" bar::bar() { /* make use of getTheAnswer() */ getTheAnswer(); } 

Observe que os nomes dentro de um namespace anônimo (como acima) não entram em conflito, pois parecem ser locais de unidades de tradução. na realidade eles não são, eles só têm nomes únicos para que eles não colidam. Se você realmente quiser (há pouca razão para) nomes locais de unidades de tradução (por exemplo, devido à compatibilidade com c para que o código C possa chamar sua function), você pode fazer assim:

 static void help() { /* .... */ } 

O ODR também diz que você não pode ter mais de uma definição de qualquer object ou function não-inline em um programa (classs são tipos, não objects, então não se aplicam a eles). Então você tem que observar não colocar funções não embutidas em headers, ou não colocar objects como “int foo”; nos headers. Isso causará erros de linker quando o vinculador tentar vincular as unidades de tradução, incluindo esses headers juntos.

Eu espero que eu possa te ajudar um pouco. Agora que foi uma resposta longa, há erros em algum lugar. Eu sei que uma unidade de tradução é estritamente definida de outra forma (saída do pré-processador). Mas eu acho que não acrescentaria grande valor para include isso no acima, e isso confundiria o assunto. Por favor, sinta-se livre para me dar um tapa se você encontrar bugs reais 🙂

Técnicas de Programação: Convenção de Arquivo de Cabeçalho C / CPP
Organizando arquivos de código em C e C ++

Decidir como separar seu código em classs / funções diferentes é uma das principais tarefas de programação. Há muitas orientações diferentes sobre como fazer isso e eu recomendaria ler alguns tutoriais sobre C ++ e Design Orientado a Objetos para você começar.

Algumas diretrizes básicas serão

  • Junte as coisas que são usadas juntas
  • Criar classs para objects de domínio (por exemplo, arquivos, collections etc)

Os arquivos de header permitem que você declare uma class ou function e, em seguida, use-a em vários arquivos de origem diferentes. Por exemplo, se você declarar uma class em um arquivo de header

 // Ah class A { public: int fn(); }; 

Você pode usar essa class em vários arquivos de origem:

 // A.cpp #include "Ah" int A::fn() {/* implementation of fn */} //B.cpp #include "Ah" void OtherFunction() { A a; a.fn(); } 

Portanto, os arquivos de header permitem separar a declaração da implementação. Se você tivesse que colocar tudo (declaração e implementação) em um arquivo de origem (por exemplo, A.cpp), tente incluí-lo em um segundo arquivo, por exemplo

 // B.cpp #include "A.cpp" //DON'T do this! 

Então você poderia compilar o B.cpp, mas quando você tenta vincular seu programa, o vinculador reclamará que você tem objects múltiplos definidos – isso é porque você tem várias cópias da implementação do A.

Sugestão: 1. Tenha um design pronto para sua aplicação agora. 2. Com base no design, crie objects necessários que interajam entre si. 3. Refatore ou altere completamente o código existente para adequar-se ao projeto recém-criado.

Os arquivos de header fornecem uma interface para as outras classs que podem usar sua funcionalidade.