Błąd „X nie nazywa typu” w C ++


124

Mam dwie klasy zadeklarowane jak poniżej:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Kiedy próbuję skompilować go za pomocą gcc, daje następujący błąd:

MyMessageBox nie nazywa typu


17
Niekończące się czasy, gdy idę ten błąd, tylko po to, aby zdać sobie sprawę, że zabezpieczenia importu generowane przez IDE są zduplikowane
Mazyod

1
Zwróć uwagę, że możesz również uzyskać ten błąd, jeśli umieścisz odwołanie zewnętrzne do deklaracji w pliku .h / .hpp przed zdefiniowaniem klasy, nawet jeśli masz rzeczywistą deklarację po włączeniu .h / .hpp w .cpp plik.
Sowa

Powinieneś także zawsze kompilować pliki C ++ za pomocą polecenia, g++a niegcc
Lorenzo Battilocchi

Odpowiedzi:


205

Kiedy kompilator kompiluje klasę Useri przechodzi do MyMessageBoxlinii, MyMessageBoxnie została jeszcze zdefiniowana. Kompilator nie ma pojęcia, że MyMessageBoxistnieje, więc nie może zrozumieć znaczenia elementu członkowskiego twojej klasy.

Musisz upewnić się, że MyMessageBoxjest zdefiniowany, zanim użyjesz go jako członka. Można to rozwiązać, odwracając kolejność definicji. Masz jednak cykliczną zależność: jeśli przejdziesz MyMessageBoxpowyżej User, to w definicji MyMessageBoxnazwy Usernie zostanie zdefiniowana!

Możesz tylko zadeklarować User ; to znaczy zadeklaruj, ale nie definiuj. Podczas kompilacji typ, który jest zadeklarowany, ale nie zdefiniowany, jest nazywany niekompletnym typem . Rozważmy prostszy przykład:

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

Deklarując do przodu User, MyMessageBoxnadal może tworzyć wskaźnik lub odniesienie do niego:

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

Nie możesz tego zrobić na odwrót: jak wspomniano, członek klasy musi mieć definicję. (Powodem jest to, że kompilator musi wiedzieć, ile pamięci Userzajmuje i wiedzieć, że musi znać rozmiar jej członków.) Gdybyś miał powiedzieć:

class MyMessageBox;

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

Nie zadziała, ponieważ nie zna jeszcze rozmiaru.


Na marginesie, ta funkcja:

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

Prawdopodobnie nie powinien brać żadnego z nich za pomocą wskaźnika. Nie możesz wysłać wiadomości bez wiadomości, ani nie możesz wysłać wiadomości bez użytkownika, do którego można ją wysłać. Obie te sytuacje można wyrazić, przekazując null jako argument do któregokolwiek z parametrów (null to doskonale poprawna wartość wskaźnika!)

Zamiast tego użyj odwołania (prawdopodobnie const):

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

3
+1 Nauczyłem się czegoś dzisiaj - pomyślałem, że zadeklarowanie w MyMessageBoxprzyszłości wystarczyłoby. A gdyby MyMessageBoxtak też była zmienna typu User- czy to byłby impas?
Amarghosh

14
@Amargosh: Tak, byłoby to niemożliwe. Logicznie niemożliwe, ponieważ Usermiałoby a, MessageBoxktóre miałoby a User, które miałoby a, MessageBoxktóre miałoby a User, które miałoby a, MessageBoxktóre miałoby a User, które miałoby a, MessageBoxktóre miałoby User...
GManNickG


3

Kompilatory C ++ przetwarzają dane wejściowe raz. Każda używana klasa musi zostać zdefiniowana jako pierwsza. Używasz MyMessageBoxzanim go zdefiniujesz. W takim przypadku możesz po prostu zamienić dwie definicje klas.


Zamiana nie będzie działać, ponieważ MyMessageBoxma Usertyp w deklaracji metody.
Amarghosh

Właściwie ta definicja nie używa klasy User. Ważne rozróżnienie, ponieważ oznacza to, że klasa User musi być tylko zadeklarowana w tym miejscu, a nie zdefiniowana . Ale zobacz obszerny post GMan.
MSalters

Tak, ale zwykła zamiana definicji nie zadziała, ponieważ Usertyp nie został jeszcze zadeklarowany.
Amarghosh

3

Musisz zdefiniować MyMessageBox przed User - ponieważ użytkownik zawiera obiekt MyMessageBox według wartości (a więc kompilator powinien znać jego rozmiar).

Będziesz także musiał przekazać dalej deklarację użytkownika przed MyMessageBox - ponieważ MyMessageBox zawiera element członkowski typu User *.


3

A propos, gdybyś miał:

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

Wtedy to również zadziała, ponieważ użytkownik jest zdefiniowany w MyMessageBox jako wskaźnik


1
Deklaracja forward to termin
benziv

1

Przed użyciem musisz zadeklarować prototyp:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

edycja : Zamieniono typy


1
Nie, nie zadziała. Składowe klasy muszą być zdefiniowane, a nie deklarowane w przód.
MSalters

1

W C ++ jest zawsze zalecane, abyś miał jedną klasę na plik nagłówkowy, zobacz tę dyskusję w SO [ 1 ]. Odpowiedzi GManNickG mówią, dlaczego tak się dzieje. Ale najlepszym sposobem rozwiązania tego problemu jest umieszczenie Userklasy w jednym pliku nagłówkowym ( User.h), a MyMessageBoxklasy w innym pliku nagłówkowym ( MyMessageBox.h). Następnie User.hwłączasz MyMessageBox.hi MyMessageBox.hwłączasz User.h. Nie zapomnij o „include gaurds” [ 2 ], aby Twój kod się pomyślnie skompilował.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.