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
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);
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
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.
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 *.
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
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.