Czym są deklaracje forward w C++?

At: http://www.learncpp.com/cpp-tutorial/19-header-files/

Wymienia się:

Dodaj.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;
}

Użyliśmy deklaracji forward, aby kompilator wiedział, czym jest "add " podczas kompilacji main.cpp. Jak wcześniej wspomniano, pisanie deklaracji do przodu dla każdej funkcji, której chcesz użyć, która mieszka w innym pliku, może szybko stać się żmudne.

Czy możesz wyjaśnić " do przodu deklaracja " dalej? Jaki jest problem, jeśli użyjemy go w funkcji main()?

Author: Robi, 2011-01-21

8 answers

Dlaczego forward-declare jest konieczne w C++

Kompilator chce upewnić się, że nie popełniłeś błędów ortograficznych lub nie przekazałeś błędnej liczby argumentów do funkcji. Dlatego nalega, aby najpierw zobaczyć deklarację 'add' (lub jakiekolwiek inne typy, klasy lub funkcje), zanim zostanie ona użyta.

To naprawdę pozwala kompilatorowi wykonać lepszą pracę walidacji kodu i pozwala mu uporządkować luźne końce, aby mógł stworzyć zgrabnie wyglądający plik obiektowy. If you didn ' t musiałby forward zadeklarować rzeczy, kompilator stworzyłby plik obiektowy, który musiałby zawierać informacje o wszystkich możliwych domysłach co do tego, czym może być funkcja 'add'. I linker musiałby zawierać bardzo sprytną logikę, aby spróbować i dowiedzieć się, który' add 'rzeczywiście zamierzałeś wywołać, gdy funkcja 'add' może żyć w innym pliku obiektowym, który łączy się z tym, który używa add do wytworzenia dll lub exe. Jest możliwe, że linker może dostać zły add. Say you chciał użyć int add( int a, float b), ale przypadkowo zapomniał go napisać, ale linker znalazł już istniejący int add (int a, int b) i pomyślał, że jest to właściwy i użył go zamiast tego. Twój kod skompilowałby się, ale nie zrobiłby tego, czego oczekiwałeś.

Tak więc, aby zachować jasność i uniknąć zgadywania itp., kompilator nalega, aby zadeklarować wszystko przed użyciem.

Różnica między deklaracją a definicją

Na marginesie, to ważne, aby znać różnicę między deklaracją a definicją. Deklaracja daje tylko tyle kodu, aby pokazać, jak coś wygląda, więc dla funkcji jest to typ zwracany, konwencja wywołania, nazwa metody, argumenty i ich typy. Ale kod dla metody nie jest wymagany. Do definicji potrzebna jest deklaracja, a następnie również kod funkcji.

W Jaki Sposób deklaracje typu forward mogą znacząco skrócić czas budowy

Możesz uzyskać deklaracja funkcji w bieżącym .cpp lub .plik h przez # includ ' ING nagłówek, który zawiera już deklarację funkcji. Jednakże, może to spowolnić kompilację, zwłaszcza jeśli # zawierać nagłówek do .h zamiast .cpp Twojego programu, jak wszystko co zawiera#.H piszesz skończy się na # include ' ING wszystkie nagłówki napisałeś # includes dla zbyt. Nagle kompilator zawiera #strony i strony kodu, które musi skompilować, nawet jeśli tylko chciałeś użyj jednej lub dwóch funkcji. Aby tego uniknąć, możesz użyć deklaracji forward I po prostu wpisać deklarację funkcji samodzielnie na górze pliku. Jeśli używasz tylko kilku funkcji, może to naprawdę sprawić, że Twoje Kompilacje będą szybsze w porównaniu do always #z nagłówkiem. W przypadku naprawdę dużych projektów różnica może wynosić godzinę lub więcej czasu kompilacji wykupionego do kilku minut.

Break cykliczne odwołania, gdzie dwie definicje oba używać każdy inne

Dodatkowo deklaracje forward mogą pomóc w przerwaniu cykli. To jest, gdzie dwie funkcje obie próbują używać siebie nawzajem. Gdy tak się stanie (i jest to całkowicie poprawne), możesz # dołączyć jeden plik nagłówka, ale ten plik nagłówka próbuje # dołączyć plik nagłówka, który aktualnie piszesz.... który następnie # zawiera drugi nagłówek, który # zawiera ten, który piszesz. Utknąłeś w sytuacji z kurczakiem i jajkiem, a każdy plik nagłówkowy próbuje ponownie włączyć #drugi. Aby rozwiązać ten problem, możesz zadeklarować potrzebne części w jednym z plików i pozostawić #include poza tym plikiem.

Eg:

/ Align = "Left" / h

#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

Koło Akt.h

Hmm... deklaracja samochodu jest wymagana tutaj, ponieważ koło ma wskaźnik do samochodu, ale samochód.h nie może być tu zawarte, ponieważ spowodowałoby to błąd kompilatora. Jeśli Samochód.H został uwzględniony, który następnie spróbuje włączyć koło.h, W tym Samochód.h, które obejmowałoby koło.h i to będzie trwać w nieskończoność, więc zamiast tego kompilator podnosi błąd. Rozwiązaniem jest forward declare Car zamiast:
class Car;     // forward declaration

class Wheel
{
    Car* car;
};

Jeśli Klasa Wheel miała metody, które muszą wywoływać metody car, metody te mogłyby być zdefiniowane w Wheel.cpp i koło.cpp jest teraz w stanie włączyć samochód.h bez powodowania cyklu.

 309
Author: Scott Langham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-02 05:20:10

Kompilator szuka każdego symbolu używanego w bieżącej jednostce tłumaczenia jest wcześniej zadeklarowany lub nie w bieżącej jednostce. Jest to tylko kwestia stylu dostarczania wszystkich podpisów metod na początku pliku źródłowego, podczas gdy definicje są dostarczane później. Znaczącym jej zastosowaniem jest użycie wskaźnika do klasy jako zmiennej członkowskiej innej klasy.

//foo.h
class bar;    // This is useful
class foo
{
    bar* obj; // Pointer or even a reference.
};

// foo.cpp
#include "bar.h"
#include "foo.h"

Więc, używaj forward-declarations w klasach, kiedy tylko jest to możliwe. Jeśli twój program posiada tylko funkcje (z nagłówkiem ho plików), a dostarczenie prototypów na początku to tylko kwestia stylu. Tak by było, gdyby plik nagłówka był obecny w normalnym programie z nagłówkiem, który ma tylko funkcje.

 24
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-21 10:31:00

Ponieważ C++ jest analizowany od góry w dół, kompilator musi wiedzieć o rzeczach, zanim zostaną użyte. Więc kiedy odwołujesz się:

int add( int x, int y )

W głównej funkcji kompilator musi wiedzieć, że istnieje. Aby to udowodnić, spróbuj przenieść go poniżej głównej funkcji, a otrzymasz błąd kompilatora.

Więc 'Forward Declaration ' jest właśnie tym, co jest napisane na puszce. Deklaruje coś przed użyciem.

Ogólnie można by dołączyć deklaracje forward w plik nagłówka, a następnie dołącz ten plik nagłówka w ten sam sposób, w jaki dołączony jest iostream .

 11
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-21 10:16:44

Termin "deklaracja terminowa" w C++ jest najczęściej używany tylko dla deklaracji klas. Zobacz (koniec) ta odpowiedź dlaczego "forward declaration" klasy jest tak naprawdę prostą deklaracją klasy z wymyślną nazwą.

Innymi słowy, "forward" po prostu dodaje balast do terminu, ponieważ Każda deklaracja może być postrzegana jako forward, o ile deklaruje jakiś identyfikator przed jest używana.

(co do jest deklaracja w przeciwieństwie do definicja, Zobacz ponownie Jaka jest różnica między definicją a deklaracją?)

 9
Author: sbi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:39

Gdy kompilator widzi add(3, 4) musi wiedzieć, co to znaczy. Z deklaracją forward można w zasadzie powiedzieć kompilatorowi, że {[1] } jest funkcją, która przyjmuje dwa int i zwraca int. Jest to ważna informacja dla kompilatora, ponieważ musi on umieścić 4 i 5 we właściwej reprezentacji na stosie i musi wiedzieć, jakiego typu jest rzecz zwracana przez add.

W tym czasie kompilator nie martwi się o rzeczywistą implementację add, czyli gdzie jest (lub jeśli jest nawet jeden) i jeśli kompiluje. To pojawia się później, po kompilacji plików źródłowych, gdy linker jest wywoływany.

 1
Author: René Nyffenegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-21 10:18:52
int add(int x, int y); // forward declaration using function prototype

Czy możesz wyjaśnić " forward declaration" dalej? Na czym polega problem, jeśli używamy go w funkcji main ()?

To to samo co #include"add.h". Jeśli wiesz, preprocesor rozszerza plik, o którym wspomniałeś w #include, w .plik cpp, w którym zapisujesz dyrektywę #include. Oznacza to, że jeśli piszesz #include"add.h", dostajesz to samo, to tak, jakbyś robił "forward declaration".

Zakładam, że add.h ma tę linię:

int add(int x, int y); 
 1
Author: Nawaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-21 10:25:41

Problem polega na tym, że kompilator nie wie, jaki rodzaj wartości jest dostarczany przez twoją funkcję; zakłada, że funkcja zwraca int w tym przypadku, ale może to być tak samo poprawne, jak może być błędne. Innym problemem jest to, że kompilator nie wie, jakiego rodzaju argumentów oczekuje twoja funkcja i nie może cię ostrzec, jeśli przekazujesz wartości niewłaściwego rodzaju. Istnieją specjalne zasady "promocji", które mają zastosowanie przy przekazywaniu, np. wartości zmiennoprzecinkowych do funkcji nierejestrowanej (kompilator musi je rozszerzyć do typu double), co często nie jest tym, czego oczekuje funkcja, co prowadzi do trudnego do znalezienia błędów w czasie wykonywania.

 0
Author: Dirk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-21 10:18:03

Jeden szybki dodatek dotyczący: zazwyczaj umieszczasz te odwołania do przodu w pliku nagłówkowym należącym do.C (pp) plik gdzie funkcja / zmienna itp. jest realizowany. w twoim przykładzie wyglądałoby to tak: add.h:

extern int add(int a, int b);

Słowo kluczowe extern stwierdza, że funkcja jest faktycznie zadeklarowana w zewnętrznym pliku (może to być również biblioteka itp.). Twoja główna.c wyglądałoby tak:

#include 
#include "add.h"

int main()
{
.
.
.

 0
Author: jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-21 10:27:12