Eu sempre vi pessoas escreverem
class.h
#ifndef CLASS_H
#define CLASS_H
//blah blah blah
#endif
A questão é: por que eles também não fazem isso para o arquivo .cpp que contém definições para funções de classe?
Digamos que sim main.cpp
e main.cpp
inclua class.h
. O class.h
arquivo não faz include
nada, então como main.cpp
saber o que há no arquivo class.cpp
?
FILE_H
, nãoCLASS_H
.Respostas:
Primeiro, para responder à sua primeira consulta:
Quando você vê isso no arquivo .h :
Essa é uma técnica de pré-processador para impedir que um arquivo de cabeçalho seja incluído várias vezes, o que pode ser problemático por vários motivos. Durante a compilação do seu projeto, cada arquivo .cpp (geralmente) é compilado. Em termos simples, isso significa que o compilador pegará seu arquivo .cpp , abrirá todos os arquivos
#included
, concatenará todos eles em um arquivo de texto maciço, executará uma análise de sintaxe e, finalmente, o converterá em código intermediário, otimizará / executará outros tarefas e, finalmente, gere a saída do assembly para a arquitetura de destino. Por esse motivo, se um arquivo estiver#included
várias vezes sob um .cpparquivo, o compilador anexará o conteúdo do arquivo duas vezes; portanto, se houver definições nesse arquivo, você receberá um erro do compilador informando que redefiniu uma variável. Quando o arquivo é processado pela etapa do pré-processador no processo de compilação, na primeira vez em que seu conteúdo é atingido, as duas primeiras linhas verificam seFILE_H
foi definido para o pré-processador. Caso contrário, ele definirá a diretiva. Na próxima vez que o conteúdo do arquivo for visto pelo pré-processador, a verificação será falsa e, portanto, será varrida imediatamente para o diretórioFILE_H
e continuará processando o código entre ele e a e continuado depois dele. Isso evita erros de redefinição.#endif
FILE_H
#endif
E para resolver sua segunda preocupação:
Na programação C ++, como prática geral, separamos o desenvolvimento em dois tipos de arquivo. Uma é com uma extensão .he chamamos isso de "arquivo de cabeçalho". Eles geralmente fornecem uma declaração de funções, classes, estruturas, variáveis globais, typedefs, macros e definições de pré-processamento, etc. Basicamente, eles apenas fornecem informações sobre seu código. Então temos a extensão .cpp que chamamos de "arquivo de código". Isso fornecerá definições para essas funções, membros da classe, quaisquer membros da estrutura que precisem de definições, variáveis globais, etc. Portanto, o arquivo .h declara código e o .cpp arquivo implementa essa declaração. Por esse motivo, geralmente durante a compilação compilamos cada .cpparquivo em um objeto e, em seguida, vincule esses objetos (porque você quase nunca vê um arquivo .cpp incluir outro arquivo .cpp ).
Como esses externos são resolvidos é um trabalho para o vinculador. Quando seu compilador processa main.cpp , ele obtém declarações para o código em class.cpp incluindo class.h . Ele só precisa saber como são essas funções ou variáveis (que é o que uma declaração fornece). Portanto, ele compila seu arquivo main.cpp em algum arquivo de objeto (chame-o de main.obj ). Da mesma forma, class.cpp é compilado em um class.objArquivo. Para produzir o executável final, um vinculador é chamado para vincular esses dois arquivos de objeto. Para quaisquer variáveis ou funções externas não resolvidas, o compilador colocará um esboço onde o acesso acontece. O vinculador pegará esse stub e procurará o código ou a variável em outro arquivo de objeto listado e, se encontrado, combina o código dos dois arquivos de objeto em um arquivo de saída e substitui o stub pelo local final da função ou variável. Dessa forma, seu código em main.cpp pode chamar funções e usar variáveis em class.cpp SE E SOMENTE SE ELES SÃO DECLARADOS EM class.h .
Eu espero que isso tenha sido útil.
fonte
O
CLASS_H
é um guarda de inclusão ; é usado para evitar que o mesmo arquivo de cabeçalho seja incluído várias vezes (por rotas diferentes) no mesmo arquivo CPP (ou, mais precisamente, na mesma unidade de tradução ), o que levaria a erros de várias definições.As proteções de inclusão não são necessárias nos arquivos CPP porque, por definição, o conteúdo do arquivo CPP é lido apenas uma vez.
Você parece ter interpretado os protetores de inclusão como tendo a mesma função que as
import
instruções em outras linguagens (como Java); esse não é o caso, no entanto. O#include
próprio é aproximadamente equivalente ao deimport
outras línguas.fonte
Não - pelo menos durante a fase de compilação.
A tradução de um programa c ++ do código fonte para o código da máquina é realizada em três fases:
class.h
é inserido no lugar da linha#include "class.h
. Como você pode incluir seu arquivo de cabeçalho em vários locais, as#ifndef
cláusulas evitam erros de declaração duplicados, pois a diretiva de pré-processador é indefinida apenas na primeira vez em que o arquivo de cabeçalho é incluído.Em resumo, as declarações podem ser compartilhadas através de um arquivo de cabeçalho, enquanto o mapeamento de declarações para definições é feito pelo vinculador.
fonte
Essa é a distinção entre declaração e definição. Os arquivos de cabeçalho geralmente incluem apenas a declaração e o arquivo de origem contém a definição.
Para usar algo, você só precisa saber que é declaração, não é definição. Somente o vinculador precisa conhecer a definição.
Portanto, é por isso que você incluirá um arquivo de cabeçalho em um ou mais arquivos de origem, mas não incluirá um arquivo de origem em outro.
Além disso, você quer dizer
#include
e não importar.fonte
Isso é feito para os arquivos de cabeçalho, para que o conteúdo apareça apenas uma vez em cada arquivo de origem pré-processado, mesmo se incluído mais de uma vez (geralmente porque está incluído em outros arquivos de cabeçalho). Na primeira vez em que é incluído, o símbolo
CLASS_H
(conhecido como guarda de inclusão ) ainda não foi definido, portanto todo o conteúdo do arquivo está incluído. Isso define o símbolo; portanto, se ele for incluído novamente, o conteúdo do arquivo (dentro do#ifndef
/#endif
bloco ) será ignorado.Não é necessário fazer isso para o próprio arquivo de origem, já que (normalmente) não está incluído em nenhum outro arquivo.
Para sua última pergunta,
class.h
deve conter a definição da classe e declarações de todos os seus membros, funções associadas e qualquer outra coisa, para que qualquer arquivo que o inclua tenha informações suficientes para usar a classe. As implementações das funções podem ir em um arquivo de origem separado; você só precisa das declarações para chamá-las.fonte
O main.cpp não precisa saber o que há no class.cpp . Ele apenas precisa conhecer as declarações das funções / classes que vai usar, e essas declarações estão em class.h .
O vinculador vincula entre os locais onde as funções / classes declaradas em class.h são usadas e suas implementações em class.cpp
fonte
.cpp
arquivos não são incluídos (usando#include
) em outros arquivos. Portanto, eles não precisam incluir proteção.Main.cpp
saberá os nomes e assinaturas da classe na qual você implementouclass.cpp
apenas porque você especificou tudo issoclass.h
- esse é o objetivo de um arquivo de cabeçalho. (Cabe a você garantir queclass.h
descreva com precisão o código no qual você implementaclass.cpp
.) O código executávelclass.cpp
será disponibilizado para o código executávelmain.cpp
graças aos esforços do vinculador.fonte
Geralmente, é esperado que módulos de código, como
.cpp
arquivos, sejam compilados uma vez e vinculados a vários projetos, para evitar a compilação repetitiva desnecessária da lógica. Por exemplo, vocêg++ -o class.cpp
produziria oclass.o
qual você poderia vincular de vários projetos ao usog++ main.cpp class.o
.Nós poderíamos usar
#include
como nosso vinculador, como você parece sugerir, mas isso seria tolo quando soubermos vincular adequadamente o uso do compilador com menos pressionamentos de teclas e menos repetições desnecessárias da compilação, em vez de nosso código com mais pressionamentos de teclas e mais desperdícios repetição de compilação ...Os arquivos de cabeçalho ainda precisam ser incluídos em cada um dos vários projetos, no entanto, porque isso fornece a interface para cada módulo. Sem esses cabeçalhos, o compilador não saberia sobre nenhum dos símbolos introduzidos pelos
.o
arquivos.É importante perceber que os arquivos de cabeçalho são o que introduz as definições de símbolos para esses módulos; Uma vez que isso é realizado, faz sentido que várias inclusões possam causar redefinições de símbolos (o que causa erros); portanto, usamos protetores para impedir essas redefinições.
fonte
por causa dos Headerfiles, define o que a classe contém (Membros, estruturas de dados) e os arquivos cpp a implementam.
E, é claro, a principal razão para isso é que você pode incluir um arquivo .h várias vezes em outros arquivos .h, mas isso resultaria em várias definições de uma classe, o que é inválido.
fonte