Erro "X não nomeia um tipo" em C ++

123

Eu tenho duas classes declaradas como abaixo:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Quando tento compilá-lo usando o gcc, ocorre o seguinte erro:

MyMessageBox não nomeia um tipo

Rakesh K
fonte
16
As infinitas vezes eu ir este erro, apenas para perceber que os guardas de importação gerados pelo IDE são duplicados
Mazyod
1
Observe que você também pode obter esse erro se colocar uma referência externa a uma declaração em um arquivo .h / .hpp antes que a classe seja definida, mesmo quando você tiver a declaração real após a inclusão de .h / .hpp no ​​arquivo .cpp Arquivo.
Owl #
Você também deve sempre compilar arquivos C ++ com o comando g++e nãogcc
Lorenzo Battilocchi

Respostas:

203

Quando o compilador compila a classe Usere chega à MyMessageBoxlinha, MyMessageBoxainda não foi definido. O compilador não tem ideiaMyMessageBox , portanto, não pode entender o significado do seu aluno.

Você precisa ter certeza de que MyMessageBoxestá definido antes de usá-lo como membro. Isso é resolvido revertendo a ordem de definição. No entanto, você tem uma dependência cíclica: se você se mover MyMessageBoxacima User, na definição do MyMessageBoxnome Usernão será definido!

O que você pode fazer é declarar adiante User ; ou seja, declare-o, mas não o defina. Durante a compilação, um tipo declarado mas não definido é chamado de tipo incompleto . Considere o exemplo mais simples:

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

Ao declarar adiante User, MyMessageBoxainda é possível formar um ponteiro ou uma referência a ele:

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

Você não pode fazer o contrário: como mencionado, um aluno precisa ter uma definição. (O motivo é que o compilador precisa saber quanta memória Userocupa e saber que precisa saber o tamanho de seus membros.) Se você disser:

class MyMessageBox;

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

Não funcionaria, pois ainda não sabe o tamanho.


Em uma nota lateral, esta função:

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

Provavelmente não deve pegar nenhum deles por ponteiro. Você não pode enviar uma mensagem sem uma mensagem, nem pode enviar uma mensagem sem um usuário para enviá-la. E ambas as situações são expressáveis ​​passando null como argumento para qualquer parâmetro (null é um valor de ponteiro perfeitamente válido!)

Em vez disso, use uma referência (possivelmente const):

 void sendMessage(const Message& msg, User& recvr);
GManNickG
fonte
3
+1 Aprendi algo hoje - pensei que declarar MyMessageBoxseria suficiente. E se MyMessageBoxtivesse uma variável do tipo Usertambém - isso seria um impasse?
Amarghosh
14
@Amargosh: Sim, seria impossível. Logicamente impossível também, já Userque teria um MessageBoxque teria um User, que teria um MessageBoxque teria um User, que teria um MessageBoxque teria um User, que teria um MessageBoxque teria um User...
GManNickG
8
  1. Encaminhar usuário declarar
  2. Coloque a declaração de MyMessageBox antes de User
Brian R. Bondy
fonte
3

Os compiladores C ++ processam suas entradas uma vez. Cada classe que você usa deve ter sido definida primeiro. Você usa MyMessageBoxantes de defini-lo. Nesse caso, você pode simplesmente trocar as duas definições de classe.

MSalters
fonte
Trocar não funcionará como MyMessageBoxtem Usertipo nele de declaração de método.
Amarghosh
Na verdade, essa definição não usa classe User. Distinção importante, porque isso significa que o usuário da classe precisa ser declarado apenas nesse ponto, não definido . Mas veja o extenso post de GMan.
MSalters
Sim, mas simplesmente trocar as definições não funcionará, pois o Usertipo ainda não está declarado.
Amarghosh 26/01
3

Você precisa definir MyMessageBox antes do Usuário - porque o Usuário inclui o objeto MyMessageBox por valor (e, portanto, o compilador deve saber seu tamanho).

Além disso, você precisará encaminhar a declaração de Usuário para MyMessageBox - porque MyMessageBox inclui membro do tipo Usuário *.

Alexander Poluektov
fonte
3

Em uma nota relacionada, se você tivesse:

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

Então isso também funcionaria, porque o usuário está definido no MyMessageBox como um ponteiro

awesomeamyg
fonte
1
Declaração forward é o termo
benziv 13/07/19
1

Você deve declarar o protótipo antes de usá-lo:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

editar : Trocou os tipos

Alex LE
fonte
1
Não, não vai funcionar. Os membros da classe devem ser definidos, não declarados antecipadamente.
precisa saber é o seguinte
1

É sempre incentivado em C ++ que você tenha uma classe por arquivo de cabeçalho; veja esta discussão no SO [ 1 ]. A resposta GManNickG diz por que isso acontece. Mas a melhor maneira de resolver isso é colocar a Userclasse em um arquivo de cabeçalho ( User.h) e a MyMessageBoxclasse em outro arquivo de cabeçalho ( MyMessageBox.h). Então User.hvocê inclui MyMessageBox.he MyMessageBox.hinclui User.h. Não esqueça de "incluir gaurds" [ 2 ] para que seu código seja compilado com sucesso.

Chehadeh
fonte