Lỗi "X không đặt tên một loại" trong C ++


124

Tôi có hai lớp được khai báo như sau:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

Khi tôi cố gắng biên dịch nó bằng gcc, nó sẽ xuất hiện lỗi sau:

MyMessageBox không đặt tên cho một loại


17
Thời gian vô tận Tôi đi lỗi này, chỉ để nhận ra rằng các lính gác nhập được tạo bởi IDE được nhân đôi
Mazyod

1
Lưu ý rằng bạn cũng có thể gặp lỗi này nếu bạn đặt tham chiếu bên ngoài đến khai báo trong tệp .h / .hpp trước khi lớp được định nghĩa, ngay cả khi bạn có khai báo thực sự sau khi bao gồm .h / .hpp trong .cpp tập tin.
Owl

Bạn cũng nên luôn biên dịch các tệp C ++ bằng lệnh g++chứ không phải lệnhgcc
Lorenzo Battilocchi

Câu trả lời:


204

Khi trình biên dịch biên dịch lớp Uservà đến MyMessageBoxdòng, MyMessageBoxvẫn chưa được xác định. Trình biên dịch không có ý tưởng MyMessageBoxtồn tại, vì vậy không thể hiểu ý nghĩa của thành viên lớp của bạn.

Bạn cần đảm bảo rằng MyMessageBoxnó đã được xác định trước khi bạn sử dụng nó như một thành viên. Điều này được giải quyết bằng cách đảo ngược thứ tự định nghĩa. Tuy nhiên, bạn có phụ thuộc theo chu kỳ: nếu bạn di chuyển MyMessageBoxở trên User, thì trong định nghĩa MyMessageBoxtên Usersẽ không được xác định!

Những gì bạn có thể làm là khai báo chuyển tiếp User ; nghĩa là khai báo nó nhưng không định nghĩa nó. Trong quá trình biên dịch, một kiểu được khai báo nhưng không được định nghĩa được gọi là kiểu không hoàn chỉnh . Hãy xem xét ví dụ đơn giản hơn:

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

Bằng cách khai báo chuyển tiếp User, MyMessageBoxvẫn có thể tạo thành một con trỏ hoặc tham chiếu đến nó:

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

Bạn không thể làm điều này theo cách khác: như đã đề cập, một thành viên trong lớp cần phải có một định nghĩa. (Lý do là trình biên dịch cần biết dung lượng bộ nhớ Userchiếm bao nhiêu và biết rằng nó cần biết kích thước của các thành viên của nó.) Nếu bạn muốn nói:

class MyMessageBox;

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

Nó sẽ không hoạt động, vì nó chưa biết kích thước.


Một lưu ý nhỏ, chức năng này:

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

Có lẽ không nên lấy một trong hai cái đó bằng con trỏ. Bạn không thể gửi tin nhắn mà không có tin nhắn, cũng như không thể gửi tin nhắn mà không có người dùng để gửi. Và cả hai trường hợp đó đều có thể diễn đạt được bằng cách chuyển null làm đối số cho một trong hai tham số (null là một giá trị con trỏ hoàn toàn hợp lệ!)

Thay vào đó, hãy sử dụng một tham chiếu (có thể là const):

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

3
+1 Hôm nay đã học được điều gì đó - Tôi nghĩ rằng tuyên bố về phía trước MyMessageBoxlà đủ. Điều gì xảy ra nếu MyMessageBoxcũng có một biến kiểu User- đó có phải là một bế tắc không?
Amarghosh

14
@Amargosh: Vâng, điều đó là không thể. Một cách logic không thể là tốt, vì Usersẽ có một MessageBoxmà sẽ có một User, trong đó sẽ có một MessageBoxmà sẽ có một User, trong đó sẽ có một MessageBoxmà sẽ có một User, trong đó sẽ có một MessageBoxmà sẽ có một User...
GManNickG

8
  1. Chuyển tiếp khai báo Người dùng
  2. Đặt khai báo của MyMessageBox trước Người dùng

3

Các trình biên dịch C ++ xử lý đầu vào của chúng một lần. Mỗi lớp bạn sử dụng phải được xác định trước. Bạn sử dụng MyMessageBoxtrước khi xác định nó. Trong trường hợp này, bạn chỉ có thể hoán đổi hai định nghĩa lớp.


Hoán đổi sẽ không hoạt động vì MyMessageBoxUserloại trong khai báo phương thức của nó.
Amarghosh

Trên thực tế, định nghĩa đó không sử dụng lớp User. Sự khác biệt quan trọng, bởi vì điều đó có nghĩa là lớp Người dùng chỉ cần được khai báo tại điểm đó, không được định nghĩa . Nhưng hãy xem bài đăng rộng rãi của GMan.
MSalters

Vâng, nhưng chỉ cần hoán đổi các định nghĩa sẽ không hoạt động vì Userkiểu chưa được khai báo.
Amarghosh

3

Bạn cần xác định MyMessageBox trước Người dùng - vì Người dùng bao gồm đối tượng của MyMessageBox theo giá trị (và vì vậy trình biên dịch nên biết kích thước của nó).

Ngoài ra, bạn sẽ cần phải chuyển tiếp khai báo Người dùng cho MyMessageBox - vì MyMessageBox bao gồm thành viên của kiểu Người dùng *.


3

Trên một ghi chú liên quan, nếu bạn có:

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

Sau đó, điều đó cũng sẽ hoạt động, vì Người dùng được định nghĩa trong MyMessageBox là một con trỏ


1
Chuyển tiếp tuyên bố là thuật ngữ
benziv

1

Bạn phải khai báo nguyên mẫu trước khi sử dụng:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

chỉnh sửa : Đã hoán đổi các loại


1
Không, sẽ không hoạt động. Các thành viên trong lớp phải được xác định, không được khai báo trước.
MSalters

1

Trong C ++ luôn khuyến khích bạn có một lớp cho mỗi tệp tiêu đề, hãy xem thảo luận này trong SO [ 1 ]. Câu trả lời của GManNickG cho biết lý do tại sao điều này xảy ra. Nhưng cách tốt nhất để giải quyết vấn đề này là đặt Userlớp vào một tệp tiêu đề ( User.h) và MyMessageBoxlớp trong tệp tiêu đề khác ( MyMessageBox.h). Sau đó, trong bạn, User.hbạn bao gồm MyMessageBox.hvà trong MyMessageBox.hbạn bao gồm User.h. Đừng quên "bao gồm bò tót" [ 2 ] để mã của bạn biên dịch thành công.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.