Fehler "X benennt keinen Typ" in C ++

122

Ich habe zwei Klassen wie folgt deklariert:

class User
{
public:
  MyMessageBox dataMsgBox;
};

class MyMessageBox
{
public:
  void sendMessage(Message *msg, User *recvr);
  Message receiveMessage();
  vector<Message> *dataMessageList;
};

Wenn ich versuche, es mit gcc zu kompilieren, wird der folgende Fehler ausgegeben:

MyMessageBox benennt keinen Typ

Rakesh K.
quelle
16
Die endlosen Zeiten, in denen ich diesen Fehler
mache
1
Beachten Sie, dass Sie diesen Fehler auch erhalten können, wenn Sie einen externen Verweis auf eine Deklaration in eine .h / .hpp-Datei einfügen, bevor die Klasse definiert wird, selbst wenn Sie die tatsächliche Deklaration nach der .h / .hpp-Aufnahme in die .cpp haben Datei.
Eule
Sie sollten auch immer C ++ - Dateien mit Befehl kompilieren g++und nichtgcc
Lorenzo Battilocchi

Antworten:

202

Wann der Compiler die Klasse kompiliert Userund zur MyMessageBoxZeile gelangt , MyMessageBoxwurde noch nicht definiert. Der Compiler hat keine Ahnung MyMessageBox, kann also die Bedeutung Ihres Klassenmitglieds nicht verstehen.

Sie müssen sicherstellen, dass MyMessageBoxes definiert ist, bevor Sie es als Mitglied verwenden. Dies wird durch Umkehren der Definitionsreihenfolge gelöst. Sie haben jedoch eine zyklische Abhängigkeit: Wenn Sie nach MyMessageBoxoben gehen User, wird in der Definition des MyMessageBoxNamens Usernicht definiert!

Was Sie tun können, ist vorwärts zu deklarieren User ; das heißt, deklarieren Sie es, aber definieren Sie es nicht. Während der Kompilierung wird ein deklarierter, aber nicht definierter Typ als unvollständiger Typ bezeichnet . Betrachten Sie das einfachere Beispiel:

struct foo; // foo is *declared* to be a struct, but that struct is not yet defined

struct bar
{
    // this is okay, it's just a pointer;
    // we can point to something without knowing how that something is defined
    foo* fp; 

    // likewise, we can form a reference to it
    void some_func(foo& fr);

    // but this would be an error, as before, because it requires a definition
    /* foo fooMember; */
};

struct foo // okay, now define foo!
{
    int fooInt;
    double fooDouble;
};

void bar::some_func(foo& fr)
{
    // now that foo is defined, we can read that reference:
    fr.fooInt = 111605;
    fr.foDouble = 123.456;
}

Durch die nach vorne erklärt User, MyMessageBoxkann immer noch einen Zeiger oder Verweis auf sie bilden:

class User; // let the compiler know such a class will be defined

class MyMessageBox
{
public:
    // this is ok, no definitions needed yet for User (or Message)
    void sendMessage(Message *msg, User *recvr); 

    Message receiveMessage();
    vector<Message>* dataMessageList;
};

class User
{
public:
    // also ok, since it's now defined
    MyMessageBox dataMsgBox;
};

Sie können dies nicht umgekehrt tun: Wie bereits erwähnt, muss ein Klassenmitglied eine Definition haben. (Der Grund dafür ist, dass der Compiler wissen muss, wie viel Speicher belegt Userwird, und dass er die Größe seiner Mitglieder kennen muss.) Wenn Sie sagen würden:

class MyMessageBox;

class User
{
public:
    // size not available! it's an incomplete type
    MyMessageBox dataMsgBox;
};

Es würde nicht funktionieren, da es die Größe noch nicht kennt.


Nebenbei bemerkt, diese Funktion:

 void sendMessage(Message *msg, User *recvr);

Wahrscheinlich sollte keiner von beiden per Zeiger genommen werden. Sie können keine Nachricht ohne Nachricht senden, noch können Sie eine Nachricht ohne einen Benutzer senden, an den Sie sie senden können. Und beide Situationen können ausgedrückt werden, indem null als Argument an einen der Parameter übergeben wird (null ist ein vollkommen gültiger Zeigerwert!).

Verwenden Sie stattdessen eine Referenz (möglicherweise const):

 void sendMessage(const Message& msg, User& recvr);
GManNickG
quelle
3
+1 Heute etwas gelernt - ich dachte, eine Vorwärtserklärung MyMessageBoxwäre genug gewesen. Was wäre, wenn MyMessageBoxes auch eine Variable vom Typ Userhätte - wäre das ein Deadlock?
Amarghosh
13
@Amargosh: Ja, das wäre unmöglich. Logisch unmöglich auch, da Userhätte MessageBoxeine User, die eine hätte, die eine MessageBoxhätte, die eine Userhätte, die eine MessageBoxhätte, die eine Userhätte, die eine MessageBoxhätte User...
GManNickG
8
  1. Benutzer weiterleiten deklarieren
  2. Stellen Sie die Deklaration von MyMessageBox vor den Benutzer
Brian R. Bondy
quelle
3

C ++ - Compiler verarbeiten ihre Eingaben einmal. Jede Klasse, die Sie verwenden, muss zuerst definiert worden sein. Sie verwenden, MyMessageBoxbevor Sie es definieren. In diesem Fall können Sie einfach die beiden Klassendefinitionen austauschen.

MSalters
quelle
Das Austauschen funktioniert nicht wie MyMessageBoxder UserTyp in der Methodendeklaration.
Amarghosh
Eigentlich ist diese Definition nicht verwendet Klasse User. Wichtiger Unterschied, da dies bedeutet, dass die Klasse Benutzer nur an diesem Punkt deklariert und nicht definiert werden muss . Aber siehe GMans umfangreichen Beitrag.
MSalters
Ja, aber das einfache Austauschen der Definitionen funktioniert nicht, da der UserTyp noch nicht deklariert ist.
Amarghosh
3

Sie müssen MyMessageBox vor dem Benutzer definieren, da der Benutzer das Objekt von MyMessageBox nach Wert einschließt (und der Compiler daher seine Größe kennen sollte).

Außerdem müssen Sie den Benutzer vor MyMessageBox deklarieren, da MyMessageBox ein Mitglied vom Typ Benutzer * enthält.

Alexander Poluektov
quelle
3

In einem ähnlichen Zusammenhang, wenn Sie hatten:

    class User; // let the compiler know such a class will be defined

    class MyMessageBox
    {
    public:
        User* myUser;
    };

    class User
    {
    public:
        // also ok, since it's now defined
        MyMessageBox dataMsgBox;
    };

Dann würde das auch funktionieren, da der Benutzer in MyMessageBox als Zeiger definiert ist

awesomeamyg
quelle
1
Vorwärtserklärung ist der Begriff
Benziv
1

Sie müssen den Prototyp deklarieren, bevor Sie ihn verwenden:

class User;

class MyMessageBox
{
public:
 void sendMessage(Message *msg, User *recvr);
 Message receiveMessage();
 vector<Message> *dataMessageList;
};

class User
{
public:
 MyMessageBox dataMsgBox;
};

Bearbeiten : Die Typen wurden ausgetauscht

Alex LE
quelle
1
Nein, wird nicht funktionieren. Klassenmitglieder müssen definiert und nicht vorwärts deklariert werden.
MSalters
1

In C ++ wird immer empfohlen, dass Sie eine Klasse pro Header-Datei haben. Siehe diese Diskussion in SO [ 1 ]. Die Antwort von GManNickG erklärt, warum dies passiert. Der beste Weg, dies zu lösen, besteht darin, die UserKlasse in eine Header-Datei ( User.h) und die MyMessageBoxKlasse in eine andere Header-Datei ( MyMessageBox.h) zu setzen. Dann in Ihrem User.hSie umfassen MyMessageBox.hund in MyMessageBox.hIhnen enthalten User.h. Vergessen Sie nicht "include gaurds" [ 2 ], damit Ihr Code erfolgreich kompiliert wird.

Chehadeh
quelle