2009-12-11 5 views
9

Der folgende Code ist in VC++ 6 kompiliert. Ich verstehe nicht, warum ich den Kompilierungsfehler C2079: 'b' uses undefined class 'B' für den folgenden Code bekomme.Vorwärtsdeklaration der Klasse scheint nicht in C++ zu funktionieren

Klasse B Quelle

#include "B.h" 

void B::SomeFunction() 
{ 
} 

Klasse B Kopf

#include "A.h" 

struct A; 

class B 
{ 
    public: 
     A a; 
     void SomeFunction(); 
}; 

eine Header-struct

#include "B.h" 

class B; 

struct A 
{ 
    B b; 
}; 

I f Ich habe den Header der Klasse B folgendermaßen geändert, dann wird kein Fehler angezeigt. Aber die Header-Deklaration wird nicht an der Spitze sein!

Klasse B-Header mit seltsamen Kopf Erklärung

struct A; 

class B 
{ 
    public: 
     A a; 
     void SomeFunction(); 
}; 

#include "A.h" 
+0

Können Sie sie in der gleichen Header-Datei? Ich weiß, dass das keine besonders technische Antwort ist, aber Sie könnten das Ganze zusammen vermeiden, wenn es machbar ist. – ihtkwot

+0

Es spielt keine Rolle, wenn Sie beide Klassendefinitionen in dieselbe Header-Datei schreiben, eine muss immer noch an erster Stelle stehen und kann die Größe der anderen Klasse nicht kennen. – Peter

+0

danke für die Klarstellung, ich bin noch sehr neu dabei und hoffe, dass ich keine schlechten Ratschläge anbiete, die ich mir – ihtkwot

Antwort

14

Um eine Klasse oder Struktur zu definieren, muss der Compiler wissen, wie groß jede Elementvariable der Klasse ist. Eine Vorwärtsdeklaration tut dies nicht. Ich habe es nur für Zeiger und (weniger oft) Referenzen gesehen.

Darüber hinaus, was Sie hier versuchen, kann nicht getan werden. Sie können nicht eine Klasse A, die eine Objekt einer anderen Klasse B enthält, die der Klasse A. ein Objekt enthält Sie kann haben jedoch Klasse A enthalten eine Zeiger zur Klasse B, die einen Objekt enthält der Klasse A.

B.cav

#include "B.h" 

void B::SomeFunction() 
{ 
} 

B.h

#ifndef __B_h__ // idempotence - keep header from being included multiple times 
#define __B_h__ 
#include "A.h" 

class B 
{ 
public: 
    A a; 
    void SomeFunction(); 
}; 

#endif // __B_h__ 

A. h

#ifndef __A_h__ // idempotence - keep header from being included multiple times 
#define __A_h__ 
#include "B.h" 

class B; // forward declaration 

struct A 
{ 
    B *b; // use a pointer here, not an object 
}; 

#endif // __A_h__ 

Zwei Punkte. Stellen Sie zunächst sicher, dass Sie eine Form der Idempotenz verwenden, um zu verhindern, dass die Header pro Kompilierungseinheit mehrfach enthalten sind. Zweitens, verstehen Sie, dass in C++ der einzige Unterschied zwischen Klassen und Strukturen die Standardsichtbarkeitsstufe ist - Klassen verwenden standardmäßig die private Sichtbarkeit, während Strukturen standardmäßig die öffentliche Sichtbarkeit verwenden. Die folgenden Definitionen sind in C++ funktional gleichwertig.

class MyClass 
{ 
public: // classes use private visibility by default 
    int i; 
    MyClass() : i(13) { } 
}; 

struct MyStruct 
{ 
    int i; 
    MyStruct() : i(13) { } 
}; 
+5

Doppel-Unterstriche vorbehalten habe, sind für den Compiler reserviert. Verwenden Sie eine andere Namenskonvention. – GManNickG

+0

@GMan: Ich habe diese Konvention immer für meine Idempotenzmarkierungen verwendet, wenn der Compiler sie nicht automatisch generiert. Ist das spezifisch für einen Compiler oder ist es in der C++ - Spezifikation? –

+1

@Matt, es ist Teil der C++ - Spezifikation, dass solche Bezeichner reserviert sind. Ich persönlich benutze "HEADER_PATH_TO_HEADER_H_INCLUDED" oder "HEADER_PATH_TO_HEADER_INCLUDED" (wenn nicht ".h"). –

3
public: 
    A a; 

Sie versuchen, das Objekt von A zu schaffen, mit nur vorwärts Erklärung. Der Compiler kann in diesem Moment (mit nur forward decl) nicht die Größe des Objekts A bestimmen und kann daher keinen Speicher für A reservieren. Sie können also keine Objekte mit nur forward decl erstellen.

ersetzen Statt mit:

A* a; 

Pointer oder Verweis auf A ohne Definition A Klasse in Ordnung arbeiten werden.

3

Zwei Fragen springen hier auf mich.

1: Sie haben Struct A anstelle von struct A geschrieben; Notieren Sie sich das Kleinbuchstabe "s". Ihr Compiler könnte das Äquivalent berücksichtigen, aber ich denke nicht, dass es Standard C++ ist.

Sie haben eine kreisförmige Referenz zwischen A und B definiert. Jedes Objekt muss ein Objekt B enthalten, aber jedes Objekt B muss ein Objekt A enthalten! Dies ist ein Widerspruch und wird niemals so funktionieren, wie Sie es wollen. Der übliche C++ - Weg, um dieses Problem zu lösen, besteht darin, Zeiger oder Referenzen für A::b oder B::a (oder beide) zu verwenden.

0

Sie können A.h von B.h und B.h von A.h. Sie sollten mindestens Präprozessormakros verwenden:

#ifndef __A_H__ 
#define __A_H__ 

// A.h contents 

#endif 

so dass Datei nicht mehr als einmal aufgenommen werden.

+0

Im eigentlichen Code sind Präprozessor-Makros definiert, die hier jedoch nicht dargestellt werden, um den Code zu vereinfachen. – Lopper

+0

Doppel-Unterstriche sind für den Compiler reserviert. Verwenden Sie eine andere Namenskonvention. – GManNickG

0

Wenn Sie eine Instanz von A erstellen, wird eine Instanz von B (member var) erstellt, die eine Instanz von A (member var) erstellt, die eine Instanz von B erzeugt, die eine Instanz von A und so erstellt on ... Der Compiler sollte dies nicht erlauben, da er unendlich Speicher benötigt.

Um dies zu lösen, müssen entweder A oder B einen Verweis/Zeiger auf die andere Klasse verwenden.

2

Vorwärtsdeklarationen, wie

struct A; 

oder

class A; 

Einführung eines als unvollständiger Typ und es bleibt bis zum Ende der unvollständigen Definition des Typs erreicht ist. Es gibt Dinge, die du mit unvollständigen Typen und Dingen tun kannst, die du nicht tun kannst. Sie können

  1. Variablen deklarieren (oder Elemente) des Typs „Zeiger auf eine“ und „Bezugnahme auf ein“
  2. Deklarieren Funktionen, die Argumente des Typs A übernehmen oder Rückgabetyp A

Sie können ‚t

  1. Variablen deklarieren (nor Mitglieder) vom Typ A
  2. dereferenzieren Zeiger auf einem oder Zugriff auf alle Mitglieder des Verweises auf ein
  3. definieren Subklassen von A.

In Ihrem Code Sie versuchen, struct Mitglied des unvollständigen Typ zu erklären. Es ist illegal. Nur Zeiger und Referenzen sind erlaubt.