Error "X no nombra un tipo" en C ++

124

Tengo dos clases declaradas de la siguiente manera:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Cuando intento compilarlo usando gcc, aparece el siguiente error:

MyMessageBox no nombra un tipo

Rakesh K
fuente
17
Las interminables veces que tengo este error, solo para darme cuenta de que los guardias de importación generados por el IDE están duplicados
Mazyod
1
Tenga en cuenta que también puede obtener este error si coloca una referencia externa a una declaración en un archivo .h / .hpp antes de que se defina la clase, incluso cuando tiene la declaración real después de la inclusión .h / .hpp dentro del .cpp expediente.
Búho
También debe compilar siempre archivos C ++ con comando g++y nogcc
Lorenzo Battilocchi

Respuestas:

204

Cuando el compilador compila la clase Usery llega a la MyMessageBoxlínea, MyMessageBoxaún no se ha definido. El compilador no tiene idea de que MyMessageBoxexiste, por lo que no puede entender el significado de su miembro de clase.

Debe asegurarse de que MyMessageBoxesté definido antes de usarlo como miembro. Esto se resuelve invirtiendo el orden de definición. Sin embargo, tiene una dependencia cíclica: si se mueve hacia MyMessageBoxarriba User, ¡en la definición del MyMessageBoxnombre Userno se definirá!

Lo que puede hacer es declarar hacia adelante User ; es decir, declararlo pero no definirlo. Durante la compilación, un tipo que se declara pero no se define se denomina tipo incompleto . Considere el ejemplo más 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;
}

Al declarar hacia adelante User, MyMessageBoxaún puede formar un puntero o hacer referencia a él:

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

No puede hacer esto al revés: como se mencionó, un miembro de la clase necesita tener una definición. (La razón es que el compilador necesita saber cuánta memoria Userocupa y saber que necesita saber el tamaño de sus miembros). Si tuviera que decir:

class MyMessageBox;

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

No funcionaría, ya que aún no conoce el tamaño.


En una nota al margen, esta función:

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

Probablemente no debería tomar ninguno de esos por puntero. No puede enviar un mensaje sin un mensaje, ni puede enviar un mensaje sin un usuario a quien enviarlo. Y ambas situaciones se pueden expresar pasando nulo como argumento a cualquier parámetro (¡nulo es un valor de puntero perfectamente válido!)

Más bien, use una referencia (posiblemente constante):

 void sendMessage(const Message& msg, User& recvr);
GManNickG
fuente
3
+1 Aprendí algo hoy: pensé que con una declaración directa MyMessageBoxhabría sido suficiente. ¿Y si también MyMessageBoxtuviera una variable de tipo User? ¿Sería un punto muerto?
Amarghosh
14
@Amargosh: Sí, sería imposible. Lógicamente imposible también, ya Userque tendría un MessageBoxque tendría un User, que tendría un MessageBoxque tendría un User, que tendría un MessageBoxque tendría un User, que tendría un MessageBoxque tendría un User...
GManNickG
8
  1. Reenviar declarar usuario
  2. Ponga la declaración de MyMessageBox antes de User
Brian R. Bondy
fuente
3

Los compiladores de C ++ procesan su entrada una vez. Cada clase que use debe haber sido definida primero. Lo usa MyMessageBoxantes de definirlo. En este caso, simplemente puede intercambiar las dos definiciones de clase.

MSalters
fuente
El intercambio no funcionará como MyMessageBoxha Userescrito en su declaración de método.
Amarghosh
En realidad, esa definición no usa clase User. Distinción importante, porque eso significa que la clase User solo debe declararse en ese punto, no definirse . Pero vea la extensa publicación de GMan.
MSalters
Sí, pero el simple hecho de intercambiar las definiciones no funcionará ya que el Usertipo aún no está declarado.
Amarghosh
3

Debe definir MyMessageBox antes que User, porque el usuario incluye el objeto de MyMessageBox por valor (por lo que el compilador debe conocer su tamaño).

También deberá reenviar declare User antes de MyMessageBox, porque MyMessageBox incluye un miembro del tipo User *.

Alexander Poluektov
fuente
3

En una nota relacionada, si tuvieras:

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

Entonces eso también funcionaría, porque el usuario está definido en MyMessageBox como un puntero

impresionanteamyg
fuente
1
Declaración
anticipada
1

Debes declarar el prototipo antes de usarlo:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

editar : intercambiado los tipos

Alex LE
fuente
1
No, no funcionará. Los miembros de la clase deben estar definidos, no declarados hacia adelante.
MSalters
1

Siempre se recomienda en C ++ que tenga una clase por archivo de encabezado, vea esta discusión en SO [ 1 ]. La respuesta de GManNickG dice por qué sucede esto. Pero la mejor manera de resolver esto es poner la Userclase en un archivo de encabezado ( User.h) y la MyMessageBoxclase en otro archivo de encabezado ( MyMessageBox.h). Luego en tu User.hincluyes MyMessageBox.hy en MyMessageBox.htu incluyes User.h. No olvide "incluir gaurds" [ 2 ] para que su código se compile correctamente.

Chehadeh
fuente