Ошибка «X не называет тип» в C ++ - PullRequest
108 голосов
/ 25 января 2010

У меня есть два класса, объявленных как показано ниже:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Когда я пытаюсь скомпилировать его с помощью gcc, выдается следующая ошибка:

MyMessageBox не называет тип

Ответы [ 7 ]

187 голосов
/ 25 января 2010

Когда компилятор компилирует класс User и попадает в строку MyMessageBox, MyMessageBox еще не определено. Компилятор понятия не имеет, что MyMessageBox существует, поэтому не может понять значение вашего ученика.

Вам необходимо убедиться, что MyMessageBox определено , прежде чем использовать его в качестве участника. Это решается путем изменения порядка определения. Однако у вас есть циклическая зависимость: если вы переместитесь на MyMessageBox выше User, то в определении MyMessageBox имя User не будет определено!

Что вы можете сделать, это Форвард объявить User; то есть, объявите это, но не определяйте это. Во время компиляции тип, который объявлен, но не определен, называется неполный тип . Рассмотрим более простой пример:

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

При прямом объявлении User, MyMessageBox все еще может сформировать указатель или ссылку на него:

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

Вы не можете сделать это наоборот: как уже упоминалось, у члена класса должно быть определение. (Причина в том, что компилятор должен знать, сколько памяти занимает User, и знать, что ему нужно знать размер своих членов.) Если бы вы сказали:

class MyMessageBox;

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

Это не сработает, поскольку еще не знает размер.


На заметку об этой функции:

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

Вероятно, не следует брать ни один из них по указателю. Вы не можете отправить сообщение без сообщения, и при этом вы не можете отправить сообщение без пользователя, чтобы отправить его. И обе эти ситуации могут быть выражены передачей нулевого значения в качестве аргумента любому параметру (нулевое значение является абсолютно допустимым значением указателя!)

Вместо этого используйте ссылку (возможно, const):

 void sendMessage(const Message& msg, User& recvr);
8 голосов
/ 25 января 2010
  1. Вперед объявить пользователя
  2. Поместите объявление MyMessageBox перед пользователем
2 голосов
/ 09 апреля 2014

На связанной ноте, если у вас было:

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

Тогда это также будет работать, потому что пользователь определен в MyMessageBox как указатель

2 голосов
/ 25 января 2010

Вам необходимо определить MyMessageBox перед пользователем - потому что пользователь включает объект MyMessageBox в значение (и поэтому компилятор должен знать его размер).

Также вам необходимо объявить вперед Пользователь перед MyMessageBox - потому что MyMessageBox включает член типа User *.

2 голосов
/ 25 января 2010

Компиляторы C ++ обрабатывают свои данные один раз. Каждый используемый вами класс должен быть определен первым. Вы используете MyMessageBox, прежде чем определить его. В этом случае вы можете просто поменять два определения классов.

0 голосов
/ 08 ноября 2016

В C ++ всегда рекомендуется иметь один класс для каждого заголовочного файла, см. Это обсуждение в SO [ 1 ]. Ответ GManNickG говорит, почему это происходит. Но лучший способ решить эту проблему - поместить класс User в один заголовочный файл (User.h) и класс MyMessageBox в другой заголовочный файл (MyMessageBox.h). Затем в вашем User.h вы включаете MyMessageBox.h, а в MyMessageBox.h вы включаете User.h. Не забудьте включить include gaurds [ 2 ], чтобы ваш код успешно компилировался.

0 голосов
/ 25 января 2010

Вы должны объявить прототип перед использованием:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

edit : поменялись местами типы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...