Errore "X non nomina un tipo" in C ++


124

Ho due classi dichiarate come di seguito:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Quando provo a compilarlo usando gcc, dà il seguente errore:

MyMessageBox non nomina un tipo


17
Le infinite volte in cui vado questo errore, solo per rendermi conto che le guardie di importazione generate dall'IDE sono duplicate
Mazyod

1
Nota che puoi anche ottenere questo errore se inserisci un riferimento esterno a una dichiarazione in un file .h / .hpp prima che la classe sia definita, anche quando hai la dichiarazione effettiva dopo l'inclusione .h / .hpp all'interno del .cpp file.
Gufo

Dovresti anche compilare sempre file C ++ con comando g++e nongcc
Lorenzo Battilocchi

Risposte:


204

Quando il compilatore compila la classe Usere arriva alla MyMessageBoxriga, MyMessageBoxnon è stato ancora definito. Il compilatore non ha idea che MyMessageBoxesista, quindi non può capire il significato del membro della classe.

Devi assicurarti che MyMessageBoxsia definito prima di usarlo come membro. Questo viene risolto invertendo l'ordine di definizione. Tuttavia, hai una dipendenza ciclica: se ti sposti MyMessageBoxsopra User, allora nella definizione del MyMessageBoxnome Usernon verrà definito!

Quello che puoi fare è dichiarare in avanti User ; cioè dichiaralo ma non definirlo. Durante la compilazione, un tipo dichiarato ma non definito viene chiamato tipo incompleto . Considera l'esempio più semplice:

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

Con la dichiarazione in avanti User, MyMessageBoxpuò ancora formare un puntatore o un riferimento ad esso:

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

Non puoi farlo al contrario: come accennato, un membro della classe deve avere una definizione. (Il motivo è che il compilatore ha bisogno di sapere quanta memoria Useroccupa e di sapere che ha bisogno di conoscere la dimensione dei suoi membri.) Se dovessi dire:

class MyMessageBox;

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

Non funzionerebbe, poiché non conosce ancora le dimensioni.


In una nota a margine, questa funzione:

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

Probabilmente non dovrebbe prendere nessuno di questi per puntatore. Non puoi inviare un messaggio senza un messaggio, né puoi inviare un messaggio senza un utente a cui inviarlo. Ed entrambe queste situazioni sono esprimibili passando null come argomento a entrambi i parametri (null è un valore puntatore perfettamente valido!)

Piuttosto, usa un riferimento (possibilmente const):

 void sendMessage(const Message& msg, User& recvr);

3
+1 Ho imparato qualcosa oggi: pensavo che la dichiarazione anticipata MyMessageBoxsarebbe stata sufficiente. E se MyMessageBoxavesse anche una variabile di tipo User- sarebbe un deadlock?
Amarghosh

14
@ Amargosh: Sì, sarebbe impossibile. Logicamente impossibile anche, poiché Useravrebbe un MessageBoxche avrebbe un User, che avrebbe un MessageBoxche avrebbe un User, che avrebbe un MessageBoxche avrebbe un User, che avrebbe un MessageBoxche avrebbe un User...
GManNickG

8
  1. Inoltra dichiara utente
  2. Metti la dichiarazione di MyMessageBox prima di User

3

I compilatori C ++ elaborano il loro input una volta. Ogni classe che utilizzi deve essere stata definita per prima. Lo usi MyMessageBoxprima di definirlo. In questo caso, puoi semplicemente scambiare le due definizioni di classe.


Lo scambio non funzionerà come MyMessageBoxha il Usertipo nella sua dichiarazione del metodo.
Amarghosh

In realtà, quella definizione non usa la classe User. Importante distinzione, perché ciò significa che la classe User deve essere dichiarata solo a quel punto, non definita . Ma vedi l'ampio post di GMan.
MSalters

Sì, ma semplicemente scambiare le definizioni non funzionerà poiché il Usertipo non è ancora dichiarato.
Amarghosh

3

È necessario definire MyMessageBox prima di User - perché l'utente include l'oggetto di MyMessageBox per valore (e quindi il compilatore dovrebbe conoscerne le dimensioni).

Inoltre dovrai inoltrare la dichiarazione dell'utente prima di MyMessageBox, perché MyMessageBox include membri di tipo User *.


3

In una nota correlata, se avessi:

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

Allora funzionerebbe anche, perché l'utente è definito in MyMessageBox come un puntatore


1
La dichiarazione in avanti è il termine
benziv

1

È necessario dichiarare il prototipo prima di utilizzarlo:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

modifica : scambiati i tipi


1
No, non funzionerà. I membri della classe devono essere definiti, non dichiarati in avanti.
MSalters

1

È sempre incoraggiato in C ++ avere una classe per file di intestazione, vedere questa discussione in SO [ 1 ]. La risposta di GManNickG spiega perché ciò è accaduto. Ma il modo migliore per risolvere questo problema è mettere la Userclasse in un file di intestazione ( User.h) e la MyMessageBoxclasse in un altro file di intestazione ( MyMessageBox.h). Quindi nel tuo User.hincludi MyMessageBox.he nel MyMessageBox.hincludi User.h. Non dimenticare "include gaurds" [ 2 ] in modo che il tuo codice venga compilato correttamente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.