Em: http://www.learncpp.com/cpp-tutorial/19-header-files/
O seguinte é mencionado:
add.cpp:
int add(int x, int y)
{
return x + y;
}
main.cpp:
#include <iostream>
int add(int x, int y); // forward declaration using function prototype
int main()
{
using namespace std;
cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
return 0;
}
Usamos uma declaração direta para que o compilador soubesse o que "
add
" era ao compilarmain.cpp
. Como mencionado anteriormente, escrever declarações de encaminhamento para todas as funções que você deseja usar que residem em outro arquivo pode ser entediante rapidamente.
Você pode explicar mais a " declaração direta "? Qual é o problema se o usarmos na main()
função?
c++
declaration
forward-declaration
Simplicidade
fonte
fonte
Respostas:
Por que a declaração direta é necessária em C ++
O compilador deseja garantir que você não tenha cometido erros de ortografia ou passado o número errado de argumentos para a função. Portanto, ele insiste em ver primeiro uma declaração de 'add' (ou quaisquer outros tipos, classes ou funções) antes de ser usada.
Isso realmente permite que o compilador faça um trabalho melhor de validação do código e permite que ele solte pontas soltas para que possa produzir um arquivo de objeto com aparência elegante. Se você não tivesse que encaminhar declarar as coisas, o compilador produziria um arquivo de objeto que teria que conter informações sobre todas as suposições possíveis sobre qual poderia ser a função 'adicionar'. E o vinculador precisaria conter uma lógica muito inteligente para tentar descobrir qual 'add' você realmente pretendia chamar, quando a função 'add' pode viver em um arquivo de objeto diferente que o vinculador está se unindo ao que usa add para produzir uma DLL ou EXE. É possível que o vinculador receba a adição errada. Digamos que você queira usar int add (int a, float b), mas acidentalmente esqueceu de escrevê-lo, mas o vinculador encontrou um int add já existente (int a, int b) e achou que era o correto e usou isso em seu lugar. Seu código seria compilado, mas não faria o que você esperava.
Portanto, apenas para manter as coisas explícitas e evitar adivinhações, etc, o compilador insiste em que você declare tudo antes de usá-lo.
Diferença entre declaração e definição
Como um aparte, é importante saber a diferença entre uma declaração e uma definição. Uma declaração apenas fornece código suficiente para mostrar como algo se parece; portanto, para uma função, esse é o tipo de retorno, convenção de chamada, nome do método, argumentos e seus tipos. Mas o código para o método não é necessário. Para uma definição, você precisa da declaração e também do código para a função.
Como declarações futuras podem reduzir significativamente os tempos de construção
Você pode obter a declaração de uma função no seu arquivo .cpp ou .h atual # incluindo o cabeçalho que já contém uma declaração da função. No entanto, isso pode atrasar sua compilação, especialmente se você # incluir um cabeçalho em .h em vez de .cpp do seu programa, pois tudo o que #inclui o .h que você está escrevendo acabará # incluindo todos os cabeçalhos você escreveu #includes for also. De repente, o compilador inclui # páginas e páginas de código que ele precisa compilar, mesmo quando você só queria usar uma ou duas funções. Para evitar isso, você pode usar uma declaração direta e apenas digitar a declaração da função na parte superior do arquivo. Se você estiver usando apenas algumas funções, isso pode realmente tornar suas compilações mais rápidas em comparação a sempre #incluindo o cabeçalho. Para projetos realmente grandes,
Quebrar referências cíclicas em que duas definições se usam
Além disso, as declarações avançadas podem ajudá-lo a interromper os ciclos. É aqui que duas funções tentam se usar. Quando isso acontece (e é uma coisa perfeitamente válida a ser feita), você pode # incluir um arquivo de cabeçalho, mas esse arquivo de cabeçalho tenta # incluir o arquivo de cabeçalho que você está escrevendo atualmente .... que, em seguida, inclui o outro cabeçalho , que # inclui o que você está escrevendo. Você está preso em uma situação de galinha e ovo, com cada arquivo de cabeçalho tentando # incluir o outro. Para resolver isso, você pode encaminhar novamente as partes necessárias em um dos arquivos e deixar o #include fora desse arquivo.
Por exemplo:
Arquivo Car.h
Arquivo Wheel.h
Hmm ... a declaração de Car é necessária aqui, pois Wheel tem um ponteiro para um Car, mas Car.h não pode ser incluído aqui, pois resultaria em um erro do compilador. Se Car.h fosse incluído, isso tentaria incluir Wheel.h, que incluiria Car.h, que incluiria Wheel.he isso continuaria para sempre, portanto, o compilador gera um erro. A solução é encaminhar declarar Car em vez disso:
Se a classe Wheel tiver métodos que precisam chamar métodos de carro, esses métodos poderão ser definidos em Wheel.cpp e Wheel.cpp agora poderá incluir Car.h sem causar um ciclo.
fonte
// From Car.h
poderá criar algumas situações difíceis tentando encontrar uma definição no caminho, garantida.O compilador procura por cada símbolo que está sendo usado na unidade de conversão atual anteriormente declarado ou não na unidade atual. É apenas uma questão de estilo, fornecendo todas as assinaturas de métodos no início de um arquivo de origem, enquanto as definições são fornecidas posteriormente. O uso significativo disso é quando você usa um ponteiro para uma classe como variável de membro de outra classe.
Portanto, use declarações avançadas nas classes sempre que possível. Se o seu programa tiver apenas funções (com arquivos de cabeçalho ho), fornecer protótipos no início é apenas uma questão de estilo. De qualquer forma, isso ocorreria se o arquivo de cabeçalho estivesse presente em um programa normal com cabeçalho que possui apenas funções.
fonte
Como o C ++ é analisado de cima para baixo, o compilador precisa saber sobre as coisas antes de serem usadas. Então, quando você faz referência:
na função principal, o compilador precisa saber que existe. Para provar isso, tente movê-lo para abaixo da função principal e você receberá um erro do compilador.
Portanto, uma ' Declaração Avançada ' é exatamente o que diz na lata. Está declarando algo antes de seu uso.
Geralmente você incluiria declarações de encaminhamento em um arquivo de cabeçalho e, em seguida, o incluiria da mesma maneira que o iostream é incluído.
fonte
O termo " declaração direta " em C ++ é usado principalmente apenas para declarações de classe . Veja (no final) esta resposta para saber por que uma "declaração direta" de uma classe realmente é apenas uma declaração simples de classe com um nome sofisticado.
Em outras palavras, o "encaminhamento" apenas adiciona lastro ao termo, pois qualquer declaração pode ser vista como encaminhada na medida em que declara algum identificador antes de ser usado.
( Sobre o que é uma declaração e não uma definição , consulte novamente Qual é a diferença entre uma definição e uma declaração? )
fonte
Quando o compilador vê
add(3, 4)
, precisa saber o que isso significa. Com a declaração forward, você basicamente diz ao compilador queadd
é uma função que recebe duas entradas e retorna um int. Essas são informações importantes para o compilador, pois ele precisa colocar 4 e 5 na representação correta na pilha e precisa saber que tipo de coisa retornada pelo add é.Naquela época, o compilador não está preocupado com a real implementação de
add
, ou seja, onde é (ou se não é mesmo um) e se ele compila. Isso aparece depois, depois de compilar os arquivos de origem quando o vinculador é chamado.fonte
É o mesmo que
#include"add.h"
. Se você souber, o pré-processador expande o arquivo mencionado#include
, no arquivo .cpp em que você escreve a#include
diretiva. Isso significa que, se você escreve#include"add.h"
, recebe a mesma coisa, é como se estivesse fazendo uma "declaração de encaminhamento".Estou assumindo que
add.h
tem esta linha:fonte
um adendo rápido sobre: geralmente você coloca essas referências em um arquivo de cabeçalho pertencente ao arquivo .c (pp) em que a função / variável etc. é implementada. no seu exemplo, ficaria assim: add.h:
a palavra-chave extern afirma que a função é realmente declarada em um arquivo externo (também pode ser uma biblioteca etc.). seu main.c ficaria assim:
fonte
Um problema é que o compilador não sabe que tipo de valor é entregue por sua função; é assumido, que a função retorne um
int
nesse caso, mas isso pode ser tão correto quanto errado. Outro problema é que o compilador não sabe que tipo de argumento sua função espera e não pode avisá-lo se você estiver passando valores do tipo errado. Existem regras especiais de "promoção", que se aplicam ao passar, digamos valores de ponto flutuante para uma função não declarada (o compilador precisa aumentá-los para digitar double), o que geralmente não é o que a função realmente espera, levando a erros difíceis de encontrar em tempo de execução.fonte