Vorwärtsdeklaration in C++

Dhruvdeep Singh Saini 12 Oktober 2023
  1. Vorwärtsdeklaration in C++
  2. Vorwärtsdeklaration von Funktionen in C++
  3. Forward-Deklaration von Klassen in C++
  4. Warum der C++-Compiler eine Vorwärtsdeklaration benötigt
  5. Vorteile der Verwendung der Vorwärtsdeklaration in C++
Vorwärtsdeklaration in C++

Dieser Artikel erklärt Vorwärtsdeklarationen und zeigt, warum sie für den Compiler in C++ notwendig sind, zusammen mit Codebeispielen.

Außerdem werden die Vorteile der Verwendung von Vorwärtsdeklarationen erörtert, der Unterschied zwischen einer Deklaration und einer Definition hervorgehoben und gezeigt, wie die Vorwärtsdeklaration verwendet wird, um zyklische Abhängigkeitsfehler von C++-Dateien zu vermeiden.

Vorwärtsdeklaration in C++

Eine Vorwärtsdeklaration ist die Deklaration der Syntax einer Funktion, d. h. ihres Namens, ihres Rückgabetyps, ihrer Argumente und des Datentyps der Argumente, bevor Sie sie in Ihrem Programm verwenden.

Bevor wir Funktionen definieren, fügen wir Vorwärtsdeklarationen ein, um den Compiler wissen zu lassen, dass die Funktion irgendwo im Programm definiert ist. Die Vorwärtsdeklaration von Funktionen, die in einer separaten Datei verwendet werden, wird mit dem #include gebildet, um die Datei zu haben.

Vorwärtsdeklaration von Funktionen in C++

Lassen Sie uns sehen, wie die Vorwärtsdeklaration innerhalb eines Code-Snippets funktioniert.

#include <iostream>
using namespace std;

// forward declaration of sub2
int sub2(int A, int B);

int main() {
  cout << "Difference: " << sub2(25, 10);
  return 0;
}

int sub2(int A, int B)  // Defining sub2 here
{
  return A - B;
}

Ausgabe:

Difference: 15

Hier haben wir eine Funktion namens sub2, die die Differenz zweier übergebener int-Parameter zurückgibt. Wir deklarieren sub2 vor dem Hauptteil und definieren dann unsere Funktion später im Programm.

Bevor wir in die Erklärung einsteigen, ist es wichtig, den Unterschied zwischen Definition und Deklaration in C++ zu kennen.

  1. Deklaration: Eine Deklaration liefert deklarierende einfache Informationen wie den Namen der Funktion, ihre Argumente und deren Datentypen, ihren Rückgabetyp, also den Funktionsprototyp.
  2. Definition: Eine Definition enthält die Details der Funktionsdeklaration und enthält das Code-Snippet, das die Aufgabenfunktion ausführen würde.

Kommen wir nun zurück zur Forward-Deklaration. Warum wurde im obigen Programm die Vorwärtsdeklaration von sub2 benötigt?

Lassen Sie es uns dieses Mal mit demselben Code erklären, ohne die Vorwärtsdeklaration zu verwenden.

#include <iostream>
using namespace std;

int main() {
  cout << "Difference: " << sub2(25, 10);
  return 0;
}

int sub2(int A, int B)  // Defining sub2 here
{
  return A - B;
}

Ausgabe:

 error: 'sub2' was not declared in this scope
    6 |     cout << "Difference: " << sub2(25, 10);
      |                               ^~~~

Das obige Programm hat kein Problem, zeigt aber immer noch einen Fehler, dass die Funktion sub2 nicht deklariert wurde. Denn sub2 wird in Zeile 6 aufgerufen, ist aber erst später in Zeile 10 undefiniert.

Da C++ eine Top-Down-Parsing-Sprache ist, erstellt sie einen Parse-Baum von oben und muss die Funktionen im Voraus kennen, bevor sie verwendet werden. Sie müssen die Funktion nicht vor dem Aufruf definieren, aber Sie müssen sie deklarieren.

Sie können auch eine Funktion, hier sub2, vor der Hauptfunktion definieren, um solche Fehler zu vermeiden. In einem Programm mit mehreren Funktionen, die sich gegenseitig oder extern eingebundene Dateien aufrufen, würde der Fehler jedoch bestehen bleiben, weshalb wir Vorwärtsdeklarationen verwenden.

Forward-Deklaration von Klassen in C++

Sie benötigen auch eine Vorwärtsdeklaration von Klassen in C++. Lassen Sie uns zeigen, wie.

#include <iostream>
using namespace std;

// Forward declaration of classes One and Two
class One;
class Two;

class One {
  int y;

 public:
  void num(int a)  // Getting input number
  {
    y = a;
  }
  friend int sub2(One, Two);
};
class Two {
  int x;

 public:
  void num(int a)  // Getting input number
  {
    x = a;
  }
  friend int sub2(One, Two);
};
int sub2(One a, Two b)  // Subtraction of two numbers from both classes
{
  int ans = a.y - b.x;
  return ans;
}

int main() {
  Two y;
  One x;

  x.num(25);
  y.num(10);

  cout << "Difference: " << sub2(x, y);
  return 0;
}

Ausgabe:

Difference: 15

Das obige Code-Snippet enthält die Klassen One und Two, beide mit einer num-Funktion, um einen Wert zu erhalten, und einer sub2-Funktion, um zwei Zahlen zu subtrahieren.

Für das obige Programm ist eine Vorwärtsdeklaration beider Klassen erforderlich, da die Klasse Eins die Friend-Funktion sub2 mit der in ihrem Parameter erwähnten Klasse Zwei enthält.

Wenn wir die Forward-Deklaration von Klassen im obigen Code-Snippet entfernen, erhalten wir eine Fehlermeldung:

15 |    [Error] 'Two' has not been declared In function 'int sub2(One, Two)':

Der Fehler zeigt, dass der Compiler eine Vorwärtsdeklaration von Funktionen und Klassen benötigt, bevor er sie in einem Programm verwendet.

Warum der C++-Compiler eine Vorwärtsdeklaration benötigt

Die Forward-Deklaration ist notwendig, da sie dem Compiler dabei hilft, 3 Dinge sicherzustellen:

  • Das Programm ist korrekt und hat keine symbolischen Rechtschreibfehler.
  • Die Argumente der deklarierten Funktion sind korrekt.
  • Die deklarierte Funktion ist im Programm vorhanden und unten definiert.

Wenn Sie die Funktionen nicht weiterleiten, würde der Compiler eine zusätzliche Objektdatei erstellen, die Informationen mit verschiedenen Vermutungen darüber enthält, was Ihre Funktion sein könnte.

Und der Linker (ein Programm, das mehrere Objekte und Klassen in einer einzigen ausführbaren Objektdatei verknüpft) hätte ein Verknüpfungsproblem, da er möglicherweise eine vorhandene Funktion mit demselben Namen, aber mit Argumenten eines anderen Datentyps hat.

Angenommen, Sie haben eine Funktion int sub2(int a, int b). Ohne die Forward-Deklaration könnte der Linker mit einer anderen existierenden Funktion int sub2(float a, float b) verwechselt werden.

Ein Compiler validiert den Code für eine saubere Datei mit einer C++-Forward-Deklaration. Denken Sie am besten daran, dass C++ in einigen Fällen ein solches Programm kompilieren und ausführen kann.

Es würde jedoch nicht die erwartete Ausgabe liefern. Aus diesem Grund benötigt der Compiler Vorwärtsdeklarationen, bevor er sie in Ihrem Code implementiert oder verwendet.

Vorteile der Verwendung der Vorwärtsdeklaration in C++

Forward-Deklarationen helfen dem Compiler, Ihren Code besser zu validieren und Verknüpfungsprobleme zu vermeiden. Aber es hilft auch:

  1. Vermeidung von Namespace-Verschmutzung: Forward-Deklarationen helfen sicherzustellen, dass kein Code-Snippet verlegt wird, und verhindern, dass ein Namespace verschmutzt wird.

  2. Kompilierungszeit verbessern: Sie können die Deklaration einer Funktion in Ihr C++-Programm einfügen, indem Sie eine Header-Datei einfügen, und der Compiler würde alle in der Datei bereitgestellten Token analysieren, was sehr lange dauern kann. Sie können diese langwierige Verarbeitung jedoch vermeiden und anstelle der gesamten cpp-Datei die Vorwärtsdeklaration für die bestimmten Klassen verwenden, die Sie verwenden möchten.

    Dies wirkt sich möglicherweise nicht auf kleinere Codes aus, ist jedoch in bedeutenderen Projekten praktisch, da es die Kompilierungszeiten minimieren und dadurch die zeitliche Komplexität reduzieren kann. Anstatt eine ganze C++-Datei einzubinden, können Sie also eine bestimmte Klasse mit der Erweiterung .h verwenden.

  3. Namenskollision vermeiden: Vorwärtsdeklarationen tragen auch dazu bei, dass keine Kollision von Token- oder Präprozessornamen im Programm auftritt, wenn verschiedene Projekte mit übereinstimmenden Funktions- oder Klassennamen vorhanden sind.

  4. Break Cyclic Dependency: Die Forward-Deklaration einer Klasse kann zyklische Referenzen lösen, indem die bestimmten Teile, die in einer Datei benötigt werden, deklariert und der Header weggelassen wird.

Vermeiden Sie zirkuläre Abhängigkeiten mithilfe der Forward-Deklaration in C++

Zwei Klassen, die verwandt sind oder die Funktionen des anderen verwenden, erzeugen eine Zirkelbeziehung. Dies wird als zyklische oder zirkuläre Abhängigkeit bezeichnet.

Angenommen, es gibt zwei Klassen in Ihrem Programm, in denen beide die andere verwenden müssen. In diesem Fall fügen Sie eine Header-Datei für eine hinzu, aber es wird weiterhin versuchen, die Header-Datei für die andere zirkulär abhängige Klasse einzuschließen, wodurch ein Zyklus entsteht, in dem jeder Header versucht, den anderen zu haben.

Sehen wir uns an, wie man zyklische Abhängigkeiten vermeidet:

#include <iostream>
#include <vector>

#include "Two.h"  // Defining Two as it is used in One

class One {
  std::vector<Two> two;
};

int main() { return 0; }

Unser Programm hat eine weitere Datei namens Two.h mit #include eingebunden, wie es in der Klasse One verwendet wird. Das Einbinden einer class.h-Datei und nicht eines ganzen anderen Programms reduziert die Kompilierungszeiten erheblich, wie oben erläutert.

Sehen Sie sich nun den Inhalt von Two.h an.

#include "One.h"  // Defining One as it is used in Two

class Two {
  One* a;  // Two uses a pointer of One
};

Two.h enthält die Klasse Two, die einen Zeiger der Klasse One verwendet. Da jedoch beide Dateien Header enthalten, um die andere Datei einzuschließen, stecken Sie in einer zirkulären Abhängigkeit fest, in der sich beide Dateien gegenseitig aufrufen. Dies kann vermieden werden, indem eine Vorwärtsdeklaration verwendet wird, anstatt einen Header in Two.h hinzuzufügen, indem:

#include <iostream>

class One;

class Two {
  One* a;  // Two uses a pointer of One
};

Denken Sie am besten daran, dass die Vorwärtsdeklaration einer Funktion die Kenntnis des Funktionsnamens und der Argumente erfordert, die bei der Definition verwendet werden, und dass die Standardwerte für Standardparameter dupliziert werden müssen.

Daher müssen Sie darauf achten, wenn Sie die Vorwärtsdeklaration in Ihren Programmen verwenden.