ข้อผิดพลาด“ X ไม่ตั้งชื่อประเภท” ใน C ++


124

ฉันมีสองคลาสที่ประกาศดังนี้:

class User
{
public:
  MyMessageBox dataMsgBox;
};

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

เมื่อฉันพยายามรวบรวมโดยใช้ gcc มันให้ข้อผิดพลาดต่อไปนี้:

MyMessageBox ไม่ได้ตั้งชื่อประเภท


17
เวลาที่ไม่มีที่สิ้นสุดที่ฉันใช้ข้อผิดพลาดนี้เพียงเพื่อให้ทราบว่าเจ้าหน้าที่นำเข้าที่สร้างโดย IDE ซ้ำกัน
Mazyod

1
โปรดทราบว่าคุณสามารถรับข้อผิดพลาดนี้ได้หากคุณวางการอ้างอิงภายนอกไปยังการประกาศในไฟล์. h / .hpp ก่อนที่คลาสจะถูกกำหนดแม้ว่าคุณจะมีการประกาศจริงหลังจากการรวม. h / .hpp ภายใน. cpp ไฟล์.
นกฮูก

คุณควรรวบรวมไฟล์ C ++ ด้วยคำสั่งเสมอg++ไม่ใช่gcc
Lorenzo Battilocchi

คำตอบ:


204

เมื่อคอมไพเลอร์คอมไพเลอร์คลาส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);

อาจไม่ควรใช้ตัวชี้อย่างใดอย่างหนึ่ง คุณไม่สามารถส่งข้อความโดยไม่มีข้อความและคุณไม่สามารถส่งข้อความโดยที่ผู้ใช้ไม่ต้องส่งให้ และทั้งสองสถานการณ์สามารถแสดงออกได้โดยการส่ง null เป็นอาร์กิวเมนต์ไปยังพารามิเตอร์ใดพารามิเตอร์หนึ่ง (null คือค่าตัวชี้ที่ถูกต้องอย่างสมบูรณ์!)

ค่อนข้างใช้การอ้างอิง (อาจเป็น const):

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

3
+1 ได้เรียนรู้บางสิ่งในวันนี้ - ฉันคิดว่าการประกาศไปข้างหน้าMyMessageBoxคงจะเพียงพอแล้ว จะเกิดอะไรขึ้นถ้าMyMessageBoxมีตัวแปรประเภทUserด้วย - นั่นจะเป็นทางตันหรือไม่?
Amarghosh

14
@Amargosh: ใช่มันจะเป็นไปไม่ได้ เหตุผลเป็นไปไม่ได้เช่นกันเนื่องจากUserจะมีMessageBoxซึ่งจะมีUserซึ่งจะมีMessageBoxซึ่งจะมีUserซึ่งจะมีMessageBoxซึ่งจะมีUserซึ่งจะมีMessageBoxที่จะมีUser...
GManNickG


3

คอมไพเลอร์ C ++ ประมวลผลอินพุตครั้งเดียว แต่ละคลาสที่คุณใช้ต้องได้รับการกำหนดก่อน คุณใช้MyMessageBoxก่อนกำหนด ในกรณีนี้คุณสามารถสลับคำจำกัดความสองคลาสได้


การแลกเปลี่ยนจะไม่ทำงานตามMyMessageBoxที่มีการUserพิมพ์ในมันประกาศวิธี
Amarghosh

อันที่จริงความหมายที่ไม่ได้ใช้Userในชั้นเรียน แตกต่างที่สำคัญเพราะนั่นหมายถึงผู้ใช้ระดับเพียงต้องการที่จะประกาศที่จุดที่ไม่ได้กำหนดไว้ แต่ดูโพสต์ที่กว้างขวางของ GMan
MSalters

ใช่ แต่การแลกเปลี่ยนคำจำกัดความจะไม่ทำงานเนื่องจากUserยังไม่ได้ประกาศประเภท
Amarghosh

3

คุณต้องกำหนด MyMessageBox ก่อน User - เนื่องจาก User รวม object ของ MyMessageBox ตามค่า (ดังนั้นคอมไพเลอร์ควรทราบขนาดของมัน)

นอกจากนี้คุณจะต้องส่งต่อประกาศ User befor MyMessageBox - เนื่องจาก MyMessageBox มีสมาชิกประเภท User *


3

ในบันทึกที่เกี่ยวข้องหากคุณมี:

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

จากนั้นก็ใช้งานได้เช่นกันเนื่องจาก User ถูกกำหนดใน MyMessageBox เป็นตัวชี้


1
การประกาศล่วงหน้าคือระยะเวลา
benziv

1

คุณต้องประกาศต้นแบบก่อนใช้งาน:

class User;

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

class User
{
public:
 MyMessageBox dataMsgBox;
};

แก้ไข : สลับประเภท


1
ไม่จะไม่ทำงาน ต้องกำหนดสมาชิกชั้นเรียนไม่ใช่ประกาศล่วงหน้า
MSalters

1

ขอแนะนำให้ใช้ C ++ เสมอว่าคุณมีหนึ่งคลาสต่อไฟล์ส่วนหัวโปรดดูการสนทนานี้ใน SO [ 1 ] คำตอบของ GManNickG จะบอกว่าเหตุใดจึงเกิดขึ้น แต่วิธีที่ดีที่สุดในการแก้ปัญหานี้คือใส่Userคลาสไว้ในไฟล์ส่วนหัวหนึ่งไฟล์ ( User.h) และMyMessageBoxคลาสในไฟล์ส่วนหัวอื่น ( MyMessageBox.h) จากนั้นUser.hคุณรวมMyMessageBox.hและในตัวMyMessageBox.hคุณUser.hด้วย อย่าลืม "รวม gaurds" [ 2 ] เพื่อให้โค้ดของคุณคอมไพล์สำเร็จ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.