web-dev-qa-db-fra.com

Erreur "X ne nomme pas un type" en C ++

J'ai deux classes déclarées comme ci-dessous:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Lorsque j'essaie de le compiler avec gcc, cela donne l'erreur suivante:

MyMessageBox ne nomme pas un type

111
Rakesh K

Lorsque le compilateur compile la classe User et parvient à la ligne MyMessageBox, MyMessageBox n'a pas encore été défini. Le compilateur n'a aucune idée que MyMessageBox existe, il est donc impossible de comprendre le sens de votre membre de classe.

Vous devez vous assurer que MyMessageBox est défini avant vous l'utilisez en tant que membre. Ceci est résolu en inversant l'ordre de définition. Cependant, vous avez une dépendance cyclique: si vous déplacez MyMessageBox au-dessus de User, alors dans la définition de MyMessageBox, le nom User ne sera pas défini!

Ce que vous pouvez faire est déclarer en avantUser; c'est-à-dire, déclarez-le mais ne le définissez pas. Lors de la compilation, un type déclaré mais non défini est appelé type incomplet. Prenons l'exemple plus simple:

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

En déclarant User, MyMessageBox peut toujours former un pointeur ou une référence sur celui-ci:

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

Vous ne pouvez pas faites l'inverse: comme mentionné, un membre de la classe doit avoir une définition. (La raison en est que le compilateur a besoin de connaître la quantité de mémoire utilisée par User et de savoir qu'il a besoin de connaître la taille de ses membres.) Si vous deviez dire:

class MyMessageBox;

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

Cela ne fonctionnerait pas, car il ne connaissait pas encore la taille.


Sur une note de côté, cette fonction:

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

Ne devrait probablement pas prendre l'un ou l'autre par pointeur. Vous ne pouvez pas envoyer de message sans message ni envoyer un message sans que l'utilisateur ne l'envoie. Et ces deux situations sont exprimables en passant null comme argument à l'un ou l'autre paramètre (null est une valeur de pointeur parfaitement valide!)

Utilisez plutôt une référence (éventuellement const):

 void sendMessage(const Message& msg, User& recvr);
189
GManNickG
  1. Forward déclarer l'utilisateur
  2. Placez la déclaration de MyMessageBox avant l'utilisateur
8
Brian R. Bondy

Sur une note connexe, si vous aviez:

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

Ensuite, cela fonctionnerait également, car l'utilisateur est défini dans MyMessageBox en tant que pointeur

2
awesomeamyg

Les compilateurs C++ traitent leur entrée une fois. Chaque classe que vous utilisez doit avoir été définie en premier. Vous utilisez MyMessageBox avant de le définir. Dans ce cas, vous pouvez simplement échanger les deux définitions de classe.

2
MSalters

Vous devez définir MyMessageBox avant User - car User inclut un objet de MyMessageBox par valeur (et le compilateur devrait donc connaître sa taille).

Aussi, vous aurez besoin de forward declare Utilisateur avant MyMessageBox - car MyMessageBox inclut un membre de type User *.

2
Alexander Poluektov

Vous devez déclarer le prototype avant de l'utiliser:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

edit: Échangé les types

0
Alex LE

Il est toujours recommandé en C++ d'avoir une classe par fichier d'en-tête, voir cette discussion dans SO [ 1 ]. La réponse de GManNickG explique pourquoi cela se produit. Mais le meilleur moyen de résoudre ce problème consiste à placer User class dans un fichier d'en-tête (User.h) et MyMessageBox class dans un autre fichier d'en-tête (MyMessageBox.h). Ensuite, dans votre User.h, vous incluez MyMessageBox.h et dans MyMessageBox.h, vous incluez User.h. N'oubliez pas "include gaurds" [ 2 ] pour que votre code soit compilé avec succès.

0
Chehadeh