Was sind Vorwärtsdeklarationen in C++?

Lesezeit: 9 Minuten

Was sind Vorwartsdeklarationen in C
Einfachheit

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

Folgendes wird erwähnt:

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

Wir haben eine Vorwärtsdeklaration verwendet, damit der Compiler weiß, was “add” war beim Kompilieren main.cpp. Wie bereits erwähnt, kann das Schreiben von Forward-Deklarationen für jede Funktion, die Sie verwenden möchten und die sich in einer anderen Datei befindet, schnell mühsam werden.

Können Sie erklären “Vorwärtserklärung” weiter? Was ist das Problem, wenn wir es in der verwenden main() Funktion?

  • Eine “Vorwärtsdeklaration” ist wirklich nur eine Deklaration. Siehe (das Ende) dieser Antwort: stackoverflow.com/questions/1410563/…

    – sbi

    21. Januar 2011 um 10:33 Uhr


1647283810 9 Was sind Vorwartsdeklarationen in C
Scott Langham

Warum die Vorwärtsdeklaration in C++ notwendig ist

Der Compiler möchte sicherstellen, dass Sie keine Rechtschreibfehler gemacht oder die falsche Anzahl von Argumenten an die Funktion übergeben haben. Es besteht also darauf, dass es zuerst eine Deklaration von „add“ (oder anderen Typen, Klassen oder Funktionen) sieht, bevor es verwendet wird.

Dies ermöglicht dem Compiler wirklich nur, den Code besser zu validieren und lose Enden zu beseitigen, sodass er eine ordentlich aussehende Objektdatei erstellen kann. Wenn Sie keine deklarierten Dinge weiterleiten müssten, würde der Compiler eine Objektdatei erzeugen, die Informationen über alle möglichen Vermutungen bezüglich der Funktion enthalten müsste add könnte sein. Und der Linker müsste eine sehr clevere Logik enthalten, um herauszufinden, welche add du wolltest eigentlich anrufen, wenn die add Die Funktion befindet sich möglicherweise in einer anderen Objektdatei, die der Linker mit derjenigen verbindet, die add verwendet, um a zu erzeugen dll oder exe. Es ist möglich, dass der Linker einen Fehler macht add. Sagen Sie, Sie wollten verwenden int add(int a, float b)aber versehentlich vergessen, es zu schreiben, aber der Linker hat ein bereits vorhandenes gefunden int add(int a, int b) und dachte, das sei das Richtige und benutzte stattdessen das. Ihr Code würde kompilieren, aber nicht das tun, was Sie erwartet haben.

Um die Dinge explizit zu halten und Vermutungen usw. zu vermeiden, besteht der Compiler darauf, dass Sie alles deklarieren, bevor es verwendet wird.

Unterschied zwischen Deklaration und Definition

Nebenbei ist es wichtig, den Unterschied zwischen einer Deklaration und einer Definition zu kennen. Eine Deklaration liefert gerade genug Code, um zu zeigen, wie etwas aussieht. Für eine Funktion sind dies also der Rückgabetyp, die Aufrufkonvention, der Methodenname, die Argumente und ihre Typen. Der Code für die Methode ist jedoch nicht erforderlich. Für eine Definition braucht man die Deklaration und dann auch noch den Code für die Funktion.

Wie Forward-Deklarationen die Build-Zeiten erheblich verkürzen können

Sie können die Deklaration einer Funktion in Ihren Strom bekommen .cpp oder .h Datei, indem Sie den Header #einschließen, der bereits eine Deklaration der Funktion enthält. Dies kann jedoch Ihre Kompilierung verlangsamen, insbesondere wenn Sie #include eine Überschrift in a .h anstatt .cpp Ihres Programms, wie alles, was die #beinhaltet .h Sie würden am Ende alle Header #include schreiben, für die Sie #includes geschrieben haben. Plötzlich hat der Compiler Seiten und Seiten mit Code eingeschlossen, die er kompilieren muss, selbst wenn Sie nur eine oder zwei Funktionen verwenden wollten. Um dies zu vermeiden, können Sie eine Vorwärtsdeklaration verwenden und die Deklaration der Funktion einfach selbst oben in der Datei eingeben. Wenn Sie nur wenige Funktionen verwenden, kann dies Ihre Kompilierungen wirklich schneller machen im Vergleich zu immer #inklusive des Headers. Bei wirklich großen Projekten kann der Unterschied eine Stunde oder mehr an Kompilierzeit betragen, die auf wenige Minuten reduziert wird.

Brechen Sie zyklische Referenzen auf, wenn sich zwei Definitionen gegenseitig verwenden

Darüber hinaus können Vorwärtsdeklarationen Ihnen helfen, Zyklen zu durchbrechen. Hier versuchen zwei Funktionen, sich gegenseitig zu verwenden. Wenn dies passiert (und es ist eine vollkommen gültige Sache), können Sie #include eine Header-Datei, aber diese Header-Datei versucht es #include die Header-Datei, die Sie gerade schreiben … die dann den anderen Header enthält, der den # enthält, den Sie gerade schreiben. Sie stecken in einer Henne-und-Ei-Situation, in der jede Header-Datei versucht, die andere wieder #einzuschließen. Um dies zu lösen, können Sie die benötigten Teile in einer der Dateien vorwärts deklarieren und das #include aus dieser Datei weglassen.

Z.B:

Datei Car.h

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

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

Datei Wheel.h

Hmm… die Deklaration von Car wird hier als benötigt Wheel hat einen Zeiger auf a Caraber Car.h kann hier nicht eingefügt werden, da dies zu einem Compilerfehler führen würde. Wenn Car.h enthalten war, würde das dann versuchen, aufzunehmen Wheel.h was beinhalten würde Car.h was beinhalten würde Wheel.h und das würde ewig so weitergehen, also löst der Compiler stattdessen einen Fehler aus. Die Lösung ist die Weiterleitungsdeklaration Car stattdessen:

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

Wenn Klasse Wheel hatte Methoden, die Methoden von aufrufen müssen Carkönnten diese Methoden in definiert werden Wheel.cpp und Wheel.cpp kann jetzt einschließen Car.h ohne einen Kreislauf zu verursachen.

  • Die Vorwärtsdeklaration ist auch erforderlich, wenn eine Funktion für zwei oder viele Klassen geeignet ist

    – Barun

    17. September 2014 um 11:13 Uhr

  • Hey Scott, zu deinem Punkt zu den Build-Zeiten: Würdest du sagen, dass es eine gängige/bewährte Methode ist, Header nach Bedarf in der .cpp-Datei immer weiterzuleiten und einzuschließen? Nach dem Lesen Ihrer Antwort scheint es so zu sein, aber ich frage mich, ob es irgendwelche Vorbehalte gibt?

    – Zepee

    11. Februar 2015 um 19:28 Uhr

  • @Zepee Es ist ein Gleichgewicht. Für schnelle Builds würde ich sagen, dass es eine gute Übung ist, und ich empfehle, es auszuprobieren. Es kann jedoch einige Anstrengungen und zusätzliche Codezeilen erfordern, die möglicherweise gewartet und aktualisiert werden müssen, wenn Typnamen usw. noch geändert werden (obwohl Tools beim automatischen Umbenennen von Dingen immer besser werden). Es gibt also einen Kompromiss. Ich habe Codebasen gesehen, wo sich niemand darum kümmert. Wenn Sie feststellen, dass Sie dieselben Forward-Definitionen wiederholen, können Sie sie immer in eine separate Header-Datei einfügen und diese einfügen, etwa so: stackoverflow.com/questions/4300696/what-is-the-iosfwd-header

    – Scott Langham

    12. Februar 2015 um 11:43 Uhr

  • Forward-Deklarationen sind erforderlich, wenn Header-Dateien aufeinander verweisen: dh stackoverflow.com/questions/396084/…

    – Nicolas Hamilton

    17. August 2015 um 1:00 Uhr

  • Ich kann sehen, dass dies den anderen Entwicklern in meinem Team ermöglicht, wirklich schlechte Bürger der Codebasis zu sein. Wenn Sie keinen Kommentar mit der Vorwärtsdeklaration benötigen, like // From Car.h Dann können Sie einige haarige Situationen schaffen, wenn Sie versuchen, später eine Definition zu finden, garantiert.

    – Dagrooms

    15. Juni 2017 um 19:57 Uhr

Der Compiler sucht nach jedem Symbol, das in der aktuellen Übersetzungseinheit verwendet wird, vorher deklariert wurde oder nicht in der aktuellen Einheit. Es ist nur eine Frage des Stils, alle Methodensignaturen am Anfang einer Quelldatei bereitzustellen, während Definitionen später bereitgestellt werden. Die signifikante Verwendung davon ist, wenn Sie einen Zeiger auf eine Klasse als Mitgliedsvariable einer anderen Klasse verwenden.

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

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

Verwenden Sie also nach Möglichkeit Vorwärtsdeklarationen in Klassen. Wenn Ihr Programm nur Funktionen hat (mit ho Header-Dateien), dann ist das Bereitstellen von Prototypen am Anfang nur eine Frage des Stils. Dies wäre jedenfalls der Fall gewesen, wenn die Header-Datei in einem normalen Programm mit Header vorhanden gewesen wäre, das nur Funktionen hat.

Da C++ von oben nach unten geparst wird, muss der Compiler über Dinge Bescheid wissen, bevor sie verwendet werden. Wenn Sie also referenzieren:

int add( int x, int y )

in der Hauptfunktion muss der Compiler wissen, dass sie existiert. Um dies zu beweisen, versuchen Sie, es unter die Hauptfunktion zu verschieben, und Sie erhalten einen Compilerfehler.

Also ein ‘Vorwärtserklärung“, steht auf der Dose. Es erklärt etwas im Voraus seiner Verwendung.

Im Allgemeinen würden Sie Vorwärtsdeklarationen in eine Header-Datei aufnehmen und dann diese Header-Datei auf die gleiche Weise einschließen iostream ist enthalten.

1647283811 584 Was sind Vorwartsdeklarationen in C
sbi

Der Begriff “Vorwärtserklärung” wird in C++ meistens nur für verwendet Klassenerklärungen. Siehe (das Ende von) dieser Antwort, warum eine “Vorwärtsdeklaration” einer Klasse wirklich nur eine einfache ist Klassenerklärung mit ausgefallenem Namen.

Mit anderen Worten, das „Forward“ fügt dem Begriff als nur Ballast hinzu irgendein Die Deklaration kann insofern als vorwärts angesehen werden, als sie einen Bezeichner deklariert Vor es ist benutzt.

(Was ist ein Erklärung im Gegensatz zu a Definitionsiehe nochmal Was ist der Unterschied zwischen einer Definition und einer Deklaration?)

Wenn der Compiler sieht add(3, 4) es muss wissen, was das bedeutet. Mit der Forward-Deklaration teilen Sie dem Compiler das im Grunde mit add ist eine Funktion, die zwei Ganzzahlen nimmt und eine Ganzzahl zurückgibt. Dies ist eine wichtige Information für den Compiler, da er 4 und 5 in der richtigen Darstellung auf den Stack legen muss und wissen muss, welchen Typ das von add zurückgegebene Ding ist.

Zu diesem Zeitpunkt kümmert sich der Compiler nicht um die tatsächlich Implementierung von adddh wo es ist (oder wenn es ist sogar eins) und wenn es kompiliert. Das kommt später in den Blick, nach dem Kompilieren der Quelldateien, wenn der Linker aufgerufen wird.

int add(int x, int y); // forward declaration using function prototype

Können Sie “Forward Declaration” näher erläutern? Was ist das Problem, wenn wir es in der Funktion main() verwenden?

Es ist dasselbe wie #include"add.h". Wenn Sie wissen, erweitert der Präprozessor die Datei, die Sie erwähnen #includein der .cpp-Datei, wo Sie die schreiben #include Richtlinie. Das heißt, wenn Sie schreiben #include"add.h"Sie erhalten dasselbe, es ist, als ob Sie eine “Vorwärtsdeklaration” durchführen würden.

Ich gehe davon aus add.h hat diese Zeile:

int add(int x, int y); 

1647283811 277 Was sind Vorwartsdeklarationen in C
Jack

Eine kurze Ergänzung zu: Normalerweise fügen Sie diese Vorwärtsreferenzen in eine Header-Datei ein, die zur .c (pp) -Datei gehört, in der die Funktion / Variable usw. implementiert ist. in deinem Beispiel würde das so aussehen: add.h:

extern int add(int a, int b);

das Schlüsselwort extern besagt, dass die Funktion eigentlich in einer externen Datei deklariert ist (könnte auch eine Bibliothek etc. sein). Ihre main.c würde so aussehen:

#include 
#include "add.h"

int main()
{
.
.
.

  • Aber setzen wir die Deklarationen nicht nur in die Header-Datei? Ich denke, deshalb ist die Funktion in “add.cpp” definiert und verwendet daher Vorwärtsdeklarationen? Danke.

    – Einfachheit

    21. Januar 2011 um 10:52 Uhr

1002780cookie-checkWas sind Vorwärtsdeklarationen in C++?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy