Kesalahan “X tidak menyebutkan jenis” di C ++

124

Saya memiliki dua kelas yang dideklarasikan sebagai berikut:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Ketika saya mencoba mengkompilasinya menggunakan gcc, itu memberikan kesalahan berikut:

MyMessageBox tidak menyebutkan sebuah tipe

Rakesh K
sumber
17
Waktu yang tidak ada habisnya saya melakukan kesalahan ini, hanya untuk menyadari bahwa penjaga impor yang dihasilkan oleh IDE diduplikasi
Mazyod
1
Perhatikan bahwa Anda juga bisa mendapatkan kesalahan ini jika Anda menempatkan referensi eksternal ke deklarasi dalam file .h / .hpp sebelum kelas ditentukan, bahkan ketika Anda memiliki deklarasi aktual setelah penyertaan .h / .hpp dalam .cpp mengajukan.
Burung Hantu
Anda juga harus selalu mengkompilasi file C ++ dengan perintah g++dan bukangcc
Lorenzo Battilocchi

Jawaban:

204

Ketika kompilator mengkompilasi kelas Userdan sampai ke MyMessageBoxbaris, MyMessageBoxbelum ditentukan. Kompilator tidak memiliki ide MyMessageBox, jadi tidak dapat memahami arti dari anggota kelas Anda.

Anda perlu memastikan MyMessageBoxsudah ditentukan sebelum Anda menggunakannya sebagai anggota. Ini diselesaikan dengan membalik urutan definisi. Namun, Anda memiliki ketergantungan siklik: jika Anda pindah ke MyMessageBoxatas User, maka definisi MyMessageBoxnamanya Usertidak akan ditentukan!

Apa yang dapat Anda lakukan adalah menyatakan ke depan User ; yaitu, nyatakan tetapi jangan definisikan. Selama kompilasi, tipe yang dideklarasikan tetapi tidak ditentukan disebut tipe tidak lengkap . Pertimbangkan contoh yang lebih sederhana:

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

Dengan mendeklarasikan terus User, MyMessageBoxmasih bisa membentuk sebuah pointer atau referensi ke sana:

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

Anda tidak dapat melakukan ini sebaliknya: seperti yang disebutkan, seorang anggota kelas perlu memiliki definisi. (Alasannya adalah bahwa kompilator perlu mengetahui berapa banyak memori yang digunakan User, dan mengetahui bahwa ia perlu mengetahui ukuran anggotanya.) Jika Anda mengatakan:

class MyMessageBox;

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

Itu tidak akan berhasil, karena belum tahu ukurannya.


Di samping catatan, fungsi ini:

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

Mungkin tidak boleh mengambil salah satu dari mereka dengan penunjuk. Anda tidak dapat mengirim pesan tanpa pesan, Anda juga tidak dapat mengirim pesan tanpa pengguna untuk mengirimkannya. Dan kedua situasi tersebut dapat diekspresikan dengan meneruskan null sebagai argumen ke salah satu parameter (null adalah nilai penunjuk yang benar-benar valid!)

Sebaliknya, gunakan referensi (mungkin const):

 void sendMessage(const Message& msg, User& recvr);
GManNickG
sumber
3
+1 Mempelajari sesuatu hari ini - Saya pikir menyatakan ke depan MyMessageBoxsudah cukup. Bagaimana jika MyMessageBoxmemiliki variabel tipe Userjuga - apakah itu akan menjadi jalan buntu?
Amarghosh
14
@Amargosh: Ya, itu tidak mungkin. Secara logis tidak mungkin juga, karena Userakan memiliki MessageBoxmana yang akan memiliki User, yang akan memiliki MessageBoxyang akan memiliki User, yang akan memiliki MessageBoxyang akan memiliki User, yang akan memiliki MessageBoxyang akan memiliki User...
GManNickG
8
  1. Teruskan deklarasikan Pengguna
  2. Letakkan deklarasi MyMessageBox sebelum Pengguna
Brian R. Bondy
sumber
3

Kompiler C ++ memproses masukan mereka satu kali. Setiap kelas yang Anda gunakan harus sudah ditentukan terlebih dahulu. Anda gunakan MyMessageBoxsebelum Anda mendefinisikannya. Dalam kasus ini, Anda cukup menukar dua definisi kelas.

MSalters
sumber
Swapping tidak akan berfungsi karena MyMessageBoxmemiliki Usertipe dalam deklarasi metodenya.
Amarghosh
Sebenarnya definisi tersebut tidak menggunakan class User. Perbedaan penting, karena itu berarti kelas Pengguna hanya perlu dideklarasikan pada saat itu, bukan ditentukan . Tapi lihat posting ekstensif GMan.
MSalters
Ya, tetapi hanya menukar definisi tidak akan berfungsi karena Usertipe belum dideklarasikan.
Amarghosh
3

Anda perlu mendefinisikan MyMessageBox sebelum User - karena User menyertakan objek MyMessageBox berdasarkan nilai (dan karenanya compiler harus mengetahui ukurannya).

Anda juga harus meneruskan deklarasi Pengguna sebelum MyMessageBox - karena MyMessageBox menyertakan anggota tipe Pengguna *.

Alexander Poluektov
sumber
3

Pada catatan terkait, jika Anda memiliki:

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

Kemudian itu juga akan berfungsi, karena Pengguna didefinisikan di MyMessageBox sebagai penunjuk

awesomeamyg
sumber
1
Deklarasi maju adalah istilah
benziv
1

Anda harus mendeklarasikan prototipe itu sebelum menggunakannya:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

edit : Menukar tipe

Alex LE
sumber
1
Tidak, tidak akan berhasil. Anggota kelas harus ditentukan, bukan diteruskan.
MSalters
1

Itu selalu didorong dalam C ++ bahwa Anda memiliki satu kelas per file header, lihat diskusi ini di SO [ 1 ]. Jawaban GManNickG menjelaskan mengapa ini terjadi. Tetapi cara terbaik untuk mengatasi ini adalah dengan meletakkan Userkelas di satu file header ( User.h) dan MyMessageBoxkelas di file header lain ( MyMessageBox.h). Kemudian dalam diri Anda User.hAnda termasuk MyMessageBox.hdan di dalam MyMessageBox.hAnda termasuk User.h. Jangan lupa "include gaurds" [ 2 ] agar kode Anda berhasil dikompilasi.

Chehadeh
sumber