Erreur «X ne nomme pas de type» en C ++

124

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 en utilisant gcc, cela donne l'erreur suivante:

MyMessageBox ne nomme pas de type

Rakesh K
la source
17
Les interminables fois où je fais cette erreur, juste pour me rendre compte que les gardes d'importation générés par l'IDE sont dupliqués
Mazyod
1
Notez que vous pouvez également obtenir cette erreur si vous placez une référence externe à une déclaration dans un fichier .h / .hpp avant que la classe ne soit définie, même si vous avez la déclaration réelle après l'inclusion .h / .hpp dans le .cpp fichier.
Owl
Vous devez également toujours compiler les fichiers C ++ avec la commande g++et nongcc
Lorenzo Battilocchi

Réponses:

204

Lorsque le compilateur compile la classe Useret accède à la MyMessageBoxligne, MyMessageBoxn'a pas encore été défini. Le compilateur n'a aucune idée de l' MyMessageBoxexistence et ne peut donc pas comprendre la signification de votre membre de classe.

Vous devez vous assurer qu'il MyMessageBoxest défini avant de l' utiliser en tant que membre. Ceci est résolu en inversant l'ordre des définitions. Cependant, vous avez une dépendance cyclique: si vous vous déplacez MyMessageBoxci User- dessus , la définition du MyMessageBoxnom Userne sera pas définie!

Ce que vous pouvez faire, c'est déclarer en avant User ; autrement dit, 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 le 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 en avant User, MyMessageBoxpeut toujours former un pointeur ou une référence à 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 faire cela dans l'autre sens: comme mentionné, un membre de la classe doit avoir une définition. (La raison est que le compilateur a besoin de savoir combien de mémoire Userprend 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 connaît pas encore la taille.


En passant, cette fonction:

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

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

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

 void sendMessage(const Message& msg, User& recvr);
GManNickG
la source
3
+1 J'ai appris quelque chose aujourd'hui - je pensais qu'une déclaration anticipée MyMessageBoxaurait suffi. Et si MyMessageBoxavait une variable de type Useraussi - serait-ce une impasse?
Amarghosh
14
@Amargosh: Oui, ce serait impossible. Logiquement impossible aussi, car Useraurait un MessageBoxqui aurait un User, qui aurait un MessageBoxqui aurait un User, qui aurait un MessageBoxqui aurait un User, qui aurait un MessageBoxqui aurait un User...
GManNickG
8
  1. Transférer déclarer l'utilisateur
  2. Placez la déclaration de MyMessageBox avant User
Brian R. Bondy
la source
3

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

MSalters
la source
Permutation ne fonctionnera pas comme MyMessageBoxa le Usertype dans elle est la déclaration de la méthode.
Amarghosh
En fait, cette définition n'utilise la classe User. Distinction importante, car cela signifie que la classe User ne doit être déclarée qu'à ce stade, non définie . Mais voyez le long article de GMan.
MSalters
Ouais, mais simplement permuter les définitions ne fonctionnera pas car le Usertype n'est pas encore déclaré.
Amarghosh
3

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

Vous devrez également transmettre la déclaration de l' utilisateur avant MyMessageBox - car MyMessageBox inclut un membre de type User *.

Alexander Poluektov
la source
3

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

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

awesomeamyg
la source
1
La déclaration avant est le terme
benziv
1

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 : permuté les types

Alex LE
la source
1
Non, ça ne marchera pas. Les membres de la classe doivent être définis et non déclarés forqward.
MSalters
1

Il est toujours recommandé en C ++ que vous ayez 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 la meilleure façon de résoudre ce problème est de mettre la Userclasse dans un fichier d'en-tête ( User.h) et la MyMessageBoxclasse dans un autre fichier d'en-tête ( MyMessageBox.h). Alors dans votre User.hvous incluez MyMessageBox.het dans MyMessageBox.hvous incluez User.h. N'oubliez pas "include gaurds" [ 2 ] pour que votre code se compile avec succès.

Chehadeh
la source