การอ้างอิงที่ไม่ได้กำหนดไปยัง vtable


357

เมื่อสร้างโปรแกรม C ++ ฉันจะได้รับข้อความแสดงข้อผิดพลาด

การอ้างอิงที่ไม่ได้กำหนดถึง 'vtable ...

สาเหตุของปัญหานี้คืออะไร? ฉันจะแก้ไขได้อย่างไร


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

หมายเหตุ: คอนสตรัคเตอร์เป็นที่ที่ข้อผิดพลาดนี้เกิดขึ้นดูเหมือนว่าจะเกิดขึ้น

รหัสของฉัน:

class CGameModule : public CDasherModule {
 public:
  CGameModule(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName)
  : CDasherModule(pEventHandler, pSettingsStore, iID, 0, szName)
  { 
      g_pLogger->Log("Inside game module constructor");   
      m_pInterface = pInterface; 
  }

  virtual ~CGameModule() {};

  std::string GetTypedTarget();

  std::string GetUntypedTarget();

  bool DecorateView(CDasherView *pView) {
      //g_pLogger->Log("Decorating the view");
      return false;
  }

  void SetDasherModel(CDasherModel *pModel) { m_pModel = pModel; }


  virtual void HandleEvent(Dasher::CEvent *pEvent); 

 private:



  CDasherNode *pLastTypedNode;


  CDasherNode *pNextTargetNode;


  std::string m_sTargetString;


  size_t m_stCurrentStringPos;


  CDasherModel *m_pModel;


  CDasherInterfaceBase *m_pInterface;
};

มรดกจาก ...

class CDasherModule;
typedef std::vector<CDasherModule*>::size_type ModuleID_t;

/// \ingroup Core
/// @{
class CDasherModule : public Dasher::CDasherComponent {
 public:
  CDasherModule(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, ModuleID_t iID, int iType, const char *szName);

  virtual ModuleID_t GetID();
  virtual void SetID(ModuleID_t);
  virtual int GetType();
  virtual const char *GetName();

  virtual bool GetSettings(SModuleSettings **pSettings, int *iCount) {
    return false;
  };

 private:
  ModuleID_t m_iID;
  int m_iType;
  const char *m_szName;
};

ซึ่งสืบทอดมาจาก ....

namespace Dasher {
  class CEvent;
  class CEventHandler;
  class CDasherComponent;
};

/// \ingroup Core
/// @{
class Dasher::CDasherComponent {
 public:
  CDasherComponent(Dasher::CEventHandler* pEventHandler, CSettingsStore* pSettingsStore);
  virtual ~CDasherComponent();

  void InsertEvent(Dasher::CEvent * pEvent);
  virtual void HandleEvent(Dasher::CEvent * pEvent) {};

  bool GetBoolParameter(int iParameter) const;
  void SetBoolParameter(int iParameter, bool bValue) const;

  long GetLongParameter(int iParameter) const;
  void SetLongParameter(int iParameter, long lValue) const;

  std::string GetStringParameter(int iParameter) const;
  void        SetStringParameter(int iParameter, const std::string & sValue) const;

  ParameterType   GetParameterType(int iParameter) const;
  std::string     GetParameterName(int iParameter) const;

 protected:
  Dasher::CEventHandler *m_pEventHandler;
  CSettingsStore *m_pSettingsStore;
};
/// @}


#endif

ฟังก์ชันใดที่ส่ง "การอ้างอิงที่ไม่ได้กำหนดไปยัง vtable ... "
J. Polfer

3
ฉันพลาดโดยสิ้นเชิงว่าข้อความแสดงข้อผิดพลาดระบุฟังก์ชั่น มันเป็นคอนสตรัคเตอร์ดังนั้นฉันเห็นชื่อคลาสของฉันและไม่ได้ทำการเชื่อมต่อ ดังนั้นผู้สร้างกำลังขว้างปาสิ่งนี้ ฉันจะเพิ่มรายละเอียดนั้นในโพสต์ดั้งเดิมของฉัน
RyanG

3
หากคุณยังไม่ได้สร้างไฟล์โครงการใหม่หลังจากทำการเปลี่ยนแปลงที่สำคัญ (เช่นqmake -projectจากนั้นqmake) เพื่อสร้างใหม่Makefileนั่นเป็นแหล่งที่มาของข้อผิดพลาดเมื่อใช้ Qt
David C. Rankin

@ DavidC.Rankin อีกปัญหาที่เกี่ยวข้องกับ Qt คือหากไฟล์ที่มีการQ_OBJECTคัดลอกภายนอก แต่ยังไม่ได้เป็นส่วนหนึ่งของไฟล์. pro ถึงแม้ว่ามันจะรวบรวมได้ดี เราต้องเพิ่มว่า.h/.cppไฟล์ลงในแฟ้ม .pro qmakeเพื่อให้สามารถ
iammilind

คำตอบ:


420

GCC คำถามที่พบบ่อยมีรายการเกี่ยวกับมัน:

การแก้ปัญหาคือเพื่อให้แน่ใจว่าวิธีเสมือนทั้งหมดที่ไม่บริสุทธิ์จะถูกกำหนด โปรดทราบว่าจะต้องกำหนด destructor แม้ว่าจะมีการประกาศ pure-virtual [class.dtor] / 7


17
nm -C CGameModule.o | grep CGameModule::จะแสดงรายการวิธีการที่กำหนดไว้สมมติว่าการใช้คลาสทั้งหมดของคุณไปยังไฟล์วัตถุโลจิคัล คุณสามารถเปรียบเทียบสิ่งนั้นกับสิ่งที่ถูกกำหนดเป็นเสมือนเพื่อค้นหาว่าคุณพลาดอะไรไป
ทรอยแดเนียลส์

132
FFS ทำไมคอมไพเลอร์ไม่ตรวจสอบสิ่งนั้นและพิมพ์ข้อผิดพลาด?
Lenar Hoyt

20
เห็นได้ชัดว่าตัวเชื่อมโยงนี้สามารถค้นพบได้เท่านั้นไม่ใช่ตัวรวบรวม
Xoph

2
ในกรณีของฉันเรามีคลาสนามธรรมที่ไม่มีการใช้งานแบบทำลายล้าง ฉันต้องทำให้การใช้งานว่างเปล่า ~ MyClass () {}
Shefy Gur-ary

1
คุณสามารถได้รับข้อผิดพลาดเช่นนี้เมื่อวัตถุที่คุณพยายามเชื่อมโยงขาดหายไปในไฟล์เก็บถาวร (ไฟล์ libxyz.a): การอ้างอิงที่ไม่ได้กำหนดกับ `vtable สำหรับ objfilename '
Kemin Zhou

162

สำหรับสิ่งที่คุ้มค่าการลืมเนื้อความใน destructor เสมือนจะสร้างสิ่งต่อไปนี้:

การอ้างอิงที่ไม่ได้กำหนดเพื่อ `vtable สำหรับ CYourClass '

ฉันกำลังเพิ่มบันทึกย่อเนื่องจากข้อความแสดงข้อผิดพลาดเป็นการหลอกลวง (นี่คือกับ gcc รุ่น 4.6.3.)


23
ฉันต้องวางเนื้อหาของตัวทำลายเสมือนที่ว่างเปล่าไว้ในไฟล์นิยาม (* .cc) อย่างชัดเจน การมีไว้ในส่วนหัวยังคงทำให้ฉันมีข้อผิดพลาด
PopcornKing

4
โปรดทราบว่าเมื่อฉันเพิ่ม destructor เสมือนลงในไฟล์การใช้งานแล้ว gcc บอกฉันว่าข้อผิดพลาดที่เกิดขึ้นจริงซึ่งเป็นเนื้อหาที่ขาดหายไปในฟังก์ชั่นอื่น
moodboom

1
@ PopcornKing ฉันเห็นปัญหาเดียวกัน แม้การกำหนด~Destructor = default;ในไฟล์ส่วนหัวก็ไม่ได้ช่วยอะไร มีข้อผิดพลาดที่เป็นเอกสารยื่นต่อ gcc หรือไม่
RD

นี่อาจเป็นปัญหาที่แตกต่างกัน แต่ปัญหาของฉันคือไม่ได้มีการใช้งานสำหรับ destructor ที่ไม่ใช่เสมือน (เปลี่ยนเป็นพอยน์เตอร์ที่ใช้ร่วมกัน / ไม่ซ้ำกันและลบออกจากไฟล์ต้นฉบับ แต่ไม่มี "การนำไปใช้" ในส่วนหัว )
svenevs

วิธีนี้ช่วยแก้ไขปัญหาของฉันได้โดยเพิ่ม {} เนื้อความว่างเปล่าสำหรับ destructor เสมือนเพื่อหลีกเลี่ยงข้อผิดพลาด
Bogdan Ionitza

56

ดังนั้นฉันจึงพบปัญหาและเป็นการรวมกันของตรรกะที่ไม่ดีและไม่คุ้นเคยกับโลก automake / autotools โดยสิ้นเชิง ฉันเพิ่มไฟล์ที่ถูกต้องไปยังเทมเพลต Makefile.am ของฉัน แต่ฉันไม่แน่ใจว่าขั้นตอนใดในกระบวนการสร้างของเรานั้นสร้าง makefile เอง ดังนั้นฉันจึงรวบรวมไฟล์เก่า ๆ ที่ไม่มีความรู้เกี่ยวกับไฟล์ใหม่ของฉันเลย

ขอขอบคุณสำหรับการตอบกลับและลิงก์ไปยังคำถามที่พบบ่อยของ GCC ฉันจะแน่ใจว่าได้อ่านเพื่อหลีกเลี่ยงปัญหานี้ที่เกิดขึ้นด้วยเหตุผลจริง


43
กล่าวโดยย่อ:. cpp ไม่รวมอยู่ในโครงสร้าง ข้อความแสดงข้อผิดพลาดทำให้เข้าใจผิดจริงๆ
Offirmo

67
สำหรับผู้ใช้ Qt: คุณสามารถได้รับข้อผิดพลาดเดียวกันนี้หากคุณลืมส่วนหัว
Chris Morlier

8
ฉันคิดว่าคุณควรยอมรับคำตอบของ Alexandre Hamez คนที่ค้นหาข้อผิดพลาดนี้น่าจะต้องการทางออกของเขาแทนที่จะเป็นของคุณ
ทิม

13
-1 นี่อาจเป็นวิธีแก้ปัญหาของคุณ แต่ไม่ใช่คำตอบสำหรับคำถามดั้งเดิม คำตอบที่ถูกต้องคือคุณไม่ได้ให้ไฟล์ออบเจกต์ด้วยสัญลักษณ์ที่ต้องการ ทำไมคุณถึงไม่ได้ให้พวกเขาเป็นอีกเรื่องหนึ่ง
วอลเตอร์

12
@ วอลเตอร์: จริงๆแล้วนี่คือคำตอบที่ฉันต้องการ คนอื่นชัดเจนและไม่ช่วยเหลือ
Edgar Bonet

50

หากคุณกำลังใช้ Qt ให้ลองทำ qmake อีกครั้ง หากข้อผิดพลาดนี้อยู่ในคลาสของวิดเจ็ต qmake อาจล้มเหลวในการสังเกตว่า ui คลาส vtable ควรถูกสร้างใหม่ ปัญหานี้ได้รับการแก้ไขสำหรับฉัน


2
ฉันเพิ่งลบทั้งโฟลเดอร์ด้วยการสร้างมันทำงานได้เช่นกัน
Tomáš Zato - Reinstate Monica

นี้จะไม่ซ้ำกับผมก็มีเหมือนกันกับqmake cmakeส่วนหนึ่งของปัญหาอาจเป็นได้ว่าเครื่องมือทั้งสองมีปัญหาเล็กน้อยกับไฟล์ส่วนหัวซึ่งอาจไม่เรียกให้มีการสร้างใหม่เมื่อจำเป็น
MSalters

2
คิดว่า "สร้างใหม่" รันใหม่ qmake โดยอัตโนมัติ ... ดูเหมือนจะไม่ ฉันทำ "Run qmake" ตามที่คุณแนะนำจากนั้น "สร้างใหม่" และแก้ไขปัญหาของฉัน
yano

45

การอ้างอิง vtable ที่ไม่ได้กำหนดอาจเกิดขึ้นเนื่องจากสถานการณ์ต่อไปนี้ด้วย ลองทำสิ่งนี้:

คลาส A ประกอบด้วย:

virtual void functionA(parameters)=0; 
virtual void functionB(parameters);

คลาส B ประกอบด้วย:

  1. คำจำกัดความของฟังก์ชั่นด้านบน A
  2. คำจำกัดความของฟังก์ชั่นด้านบน B

คลาส C ประกอบด้วย: ตอนนี้คุณกำลังเขียนคลาส C ที่คุณจะได้รับจากคลาส A

ตอนนี้ถ้าคุณพยายามที่จะรวบรวมคุณจะได้รับการอ้างอิงที่ไม่ได้กำหนดไปยัง vtable สำหรับคลาส C เป็นข้อผิดพลาด

เหตุผล:

functionAถูกกำหนดให้เป็นเวอร์ชวลเพียวและมีการกำหนดคำจำกัดความไว้ในคลาส B functionBเสมือน (ไม่บริสุทธิ์เสมือน) ดังนั้นจึงพยายามค้นหาคำจำกัดความในคลาส A แต่คุณได้ให้คำจำกัดความในคลาสบี

สารละลาย:

  1. ทำให้ฟังก์ชัน B เป็นเสมือนจริง (ถ้าคุณมีความต้องการเช่นนั้น) virtual void functionB(parameters) =0; (ใช้งานได้ผ่านการทดสอบแล้ว)
  2. ให้คำนิยามสำหรับ functionB ใน Class A ทำให้มันเป็นเสมือน (หวังว่ามันใช้งานได้เพราะฉันไม่ได้ลอง)

@ ilya1725 การแก้ไขที่แนะนำของคุณไม่เพียง แต่แก้ไขการจัดรูปแบบและสิ่งที่คล้ายกันคุณยังเปลี่ยนคำตอบด้วยเช่นกันว่าคลาส C มาจาก B แทนที่จะเป็น A และคุณกำลังเปลี่ยนโซลูชันที่สอง นี่เป็นการเปลี่ยนแปลงคำตอบอย่างมาก ในกรณีเหล่านี้โปรดแสดงความคิดเห็นกับผู้เขียนแทน ขอบคุณ!
ฟาบิโอพูดว่า Reinstate Monica

@FabioTurati classC คือ classC ที่สืบทอดมาจากไหน? ประโยคไม่ชัดเจน นอกจากนี้ความหมายของ "Class C ประกอบด้วย:" คืออะไร?
ilya1725

@ ilya1725 คำตอบนี้ไม่ชัดเจนมากและฉันไม่ได้ต่อต้านการแก้ไขและปรับปรุง สิ่งที่ฉันพูดคือการแก้ไขของคุณเปลี่ยนความหมายของคำตอบและนั่นเป็นการเปลี่ยนแปลงที่รุนแรงเกินไป หวังว่าผู้เขียนจะเข้ามาและชี้แจงสิ่งที่เขาหมายถึง (แม้ว่าเขาจะไม่ได้ใช้งานเป็นเวลานาน)
Fabio พูดว่า Reinstate Monica เมื่อ

ขอบคุณ! ในกรณีของฉันฉันมีเพียง 2 ชั้นในลำดับชั้นของฉัน คลาส A ประกาศวิธีการเสมือนบริสุทธิ์ การประกาศของคลาส B ระบุว่าจะแทนที่วิธีนี้ แต่ฉันยังไม่ได้เขียนคำจำกัดความของวิธีการ overriden เลย
Nick Desaulniers

44

ฉันเพิ่งได้รับข้อผิดพลาดนี้เพราะไฟล์ cpp ของฉันไม่ได้อยู่ใน makefile


ดูเหมือนว่าข้อความจะเปลี่ยนไปเล็กน้อยจากปกติundefined reference to {function/class/struct}เมื่อมีvirtualสิ่งที่เกี่ยวข้อง โยนฉันออกไป
Keith M

30

กคือvtableอะไร

อาจเป็นประโยชน์ที่จะทราบว่าข้อความแสดงข้อผิดพลาดกำลังพูดถึงอะไรก่อนที่จะลองแก้ไข ฉันจะเริ่มต้นในระดับสูงจากนั้นทำรายละเอียดเพิ่มเติม ด้วยวิธีนี้ผู้คนสามารถข้ามไปข้างหน้าเมื่อพวกเขาพอใจกับความเข้าใจใน vtables ... และมีผู้คนจำนวนมากกระโดดข้ามไปข้างหน้าในขณะนี้ :) สำหรับคนที่เกาะติดอยู่รอบ ๆ :

vtable เป็นพื้นการดำเนินงานที่พบมากที่สุดของความแตกต่าง ใน C ++ เมื่อใช้ vtables คลาส polymorphic ทุกตัวจะมี vtable บางส่วนในโปรแกรม คุณสามารถคิดว่ามันเป็นstaticสมาชิกข้อมูล(ซ่อน) ของชั้นเรียน วัตถุของคลาส polymorphic ทุกตัวจะเชื่อมโยงกับ vtable สำหรับคลาสที่ได้มามากที่สุด โดยการตรวจสอบการเชื่อมโยงนี้โปรแกรมสามารถทำงานเวทย์มนตร์ polymorphic ข้อแม้ที่สำคัญ: vtable คือรายละเอียดการนำไปปฏิบัติ มันไม่ได้รับคำสั่งจากมาตรฐาน C ++ แม้ว่าคอมไพเลอร์ C ++ ส่วนใหญ่ (all?) จะใช้ vtables เพื่อนำไปใช้กับพฤติกรรม polymorphic รายละเอียดที่ฉันนำเสนอมีทั้งแบบทั่วไปหรือแบบที่สมเหตุสมผล คอมไพเลอร์ได้รับอนุญาตให้เบี่ยงเบนจากนี้!

วัตถุ polymorphic แต่ละตัวมีตัวชี้ (ซ่อน) ไปยัง vtable สำหรับคลาสที่ได้มามากที่สุดของวัตถุ (อาจเป็นตัวชี้หลายตัวในกรณีที่ซับซ้อนกว่า) โดยการดูที่ตัวชี้โปรแกรมสามารถบอกได้ว่าวัตถุชนิด "ของจริง" คืออะไร (ยกเว้นระหว่างการก่อสร้าง แต่ให้ข้ามกรณีพิเศษนั้น) ตัวอย่างเช่นถ้าวัตถุของการพิมพ์ที่Aไม่ได้ชี้ไป vtable ของแล้วว่าวัตถุที่เป็นจริงย่อยวัตถุของบางสิ่งบางอย่างที่ได้มาจากAA

ชื่อ "vtable" มาจาก " v irtual function table " มันเป็นตารางที่เก็บพอยน์เตอร์ไปยังฟังก์ชั่น (เสมือน) คอมไพเลอร์เลือกแบบแผนสำหรับวิธีการจัดวางตาราง วิธีง่าย ๆ คือการทำหน้าที่เสมือนตามลำดับที่ประกาศไว้ในคำจำกัดความของชั้นเรียน เมื่อเรียกใช้ฟังก์ชันเสมือนโปรแกรมตามตัวชี้ของวัตถุไปยัง vtable ไปที่รายการที่เกี่ยวข้องกับฟังก์ชั่นที่ต้องการจากนั้นใช้ตัวชี้ฟังก์ชันที่เก็บไว้เพื่อเรียกใช้ฟังก์ชันที่ถูกต้อง มีลูกเล่นมากมายสำหรับการทำงานนี้ แต่ฉันจะไม่เข้าไปที่นี่

สร้างที่ไหน / เมื่อไหร่vtable?

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

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

กระบวนการคัดเลือกที่ใช้โดย gcc ทำให้รู้สึกว่าถ้าเราเริ่มต้นด้วยประเพณีของการอุทิศไฟล์ต้นฉบับ (เดียว) ให้กับแต่ละชั้นเรียนที่ต้องการหนึ่งสำหรับการดำเนินงานของมัน มันจะเป็นการดีที่จะปล่อย vtable เมื่อรวบรวมไฟล์ต้นฉบับนั้น เรียกว่าเป้าหมายของเรา อย่างไรก็ตามกระบวนการคัดเลือกต้องทำงานแม้ว่าจะไม่ปฏิบัติตามประเพณีนี้ ดังนั้นแทนที่จะมองหาการนำไปใช้ของคลาสทั้งหมดลองมองหาการนำไปใช้ของสมาชิกเฉพาะของคลาส หากมีการปฏิบัติตามประเพณี - ​​และหากสมาชิกนั้นนำไปปฏิบัติจริง - สิ่งนี้จะบรรลุเป้าหมาย

สมาชิกที่เลือกโดย gcc (และอาจเป็นไปได้โดยคอมไพเลอร์อื่น ๆ ) เป็นฟังก์ชันเสมือนไม่ใช่อินไลน์แรกที่ไม่ใช่เสมือนบริสุทธิ์ หากคุณเป็นส่วนหนึ่งของฝูงชนที่ประกาศตัวสร้างและนักทำลายก่อนหน้าที่สมาชิกคนอื่น ๆ ผู้ทำลายนั้นจะมีโอกาสเลือกที่ดี (คุณจำได้ว่าทำให้ผู้ทำลายระบบเสมือนจริงใช่ไหม?) มีข้อยกเว้นอยู่; ฉันคาดหวังว่าข้อยกเว้นที่พบบ่อยที่สุดคือเมื่อมีการให้คำจำกัดความแบบอินไลน์สำหรับ destructor และเมื่อมีการร้องขอ destructor เริ่มต้น (โดยใช้ " = default")

ความฉลาดอาจสังเกตเห็นว่าชั้น polymorphic ได้รับอนุญาตให้มีคำจำกัดความแบบอินไลน์สำหรับฟังก์ชั่นเสมือนทั้งหมดของมัน นั่นเป็นสาเหตุที่ทำให้กระบวนการคัดเลือกล้มเหลวหรือไม่ มันทำในคอมไพเลอร์รุ่นเก่า ฉันได้อ่านแล้วว่าคอมไพเลอร์ตัวล่าสุดได้แก้ไขสถานการณ์นี้แล้ว แต่ฉันไม่ทราบหมายเลขรุ่นที่เกี่ยวข้อง ฉันสามารถลองดูมัน แต่มันง่ายกว่าที่จะเขียนโค้ดรอบ ๆ หรือรอให้คอมไพเลอร์บ่น

โดยสรุปมีสาเหตุสำคัญสามประการของข้อผิดพลาด "การไม่ได้กำหนดการอ้างอิงถึง vtable":

  1. ฟังก์ชั่นสมาชิกขาดความหมาย
  2. ไฟล์อ็อบเจ็กต์ไม่ได้ถูกลิงก์
  3. ฟังก์ชันเสมือนทั้งหมดมีคำจำกัดความแบบอินไลน์

สาเหตุเหล่านี้เกิดจากตัวเองไม่เพียงพอที่จะทำให้เกิดข้อผิดพลาดด้วยตนเอง แต่นี่คือสิ่งที่คุณต้องการแก้ไขเพื่อแก้ไขข้อผิดพลาด อย่าคาดหวังว่าการสร้างหนึ่งในสถานการณ์เหล่านี้โดยเจตนาจะทำให้เกิดข้อผิดพลาดนี้อย่างแน่นอน มีข้อกำหนดอื่น ๆ อย่าคาดหวังว่าการแก้ไขสถานการณ์เหล่านี้จะแก้ไขข้อผิดพลาดนี้ได้

(ตกลงหมายเลข 3 อาจเพียงพอเมื่อมีการถามคำถามนี้)

จะแก้ไขข้อผิดพลาดได้อย่างไร?

ยินดีต้อนรับผู้คนย้อนกลับไปข้างหน้า! :)

  1. ดูคำจำกัดความของคลาส ค้นหาฟังก์ชันเสมือนที่ไม่ใช่อินไลน์แรกที่ไม่ใช่เสมือนบริสุทธิ์ (ไม่ใช่ " = 0") และมีคำจำกัดความที่คุณให้ไว้ (ไม่ใช่ " = default")
    • หากไม่มีฟังก์ชั่นดังกล่าวให้ลองปรับเปลี่ยนคลาสของคุณเพื่อให้มีฟังก์ชันนั้น (ข้อผิดพลาดอาจแก้ไขได้)
    • ดูคำตอบของ Philip Thomasสำหรับคำเตือน
  2. ค้นหาคำจำกัดความของฟังก์ชันนั้น หากไม่มีให้เพิ่ม! (ข้อผิดพลาดอาจแก้ไขได้)
  3. ตรวจสอบคำสั่งลิงค์ของคุณ หากไม่ได้กล่าวถึงอ็อบเจ็กต์ไฟล์ที่มีนิยามของฟังก์ชันนั้นให้แก้ไข! (ข้อผิดพลาดอาจแก้ไขได้)
  4. ทำซ้ำขั้นตอนที่ 2 และ 3 สำหรับแต่ละฟังก์ชั่นเสมือนจากนั้นสำหรับแต่ละฟังก์ชั่นที่ไม่ใช่เสมือนจนกระทั่งข้อผิดพลาดได้รับการแก้ไข หากคุณยังคงติดอยู่ให้ทำซ้ำสำหรับสมาชิกข้อมูลสแตติกแต่ละคน

ตัวอย่าง
รายละเอียดของสิ่งที่ต้องทำสามารถแตกต่างกันและบางครั้งก็แยกออกเป็นคำถามที่แยกต่างหาก (เช่นข้อผิดพลาดของสัญลักษณ์อ้างอิงภายนอกที่ยังไม่ได้แก้ไข / การแก้ไขที่ไม่ได้กำหนดไว้คืออะไรและฉันจะแก้ไขได้อย่างไร ) แม้ว่าฉันจะให้ตัวอย่างของสิ่งที่ต้องทำในกรณีเฉพาะที่อาจทำให้สับสนโปรแกรมเมอร์ใหม่

ขั้นตอนที่ 1 กล่าวถึงการปรับเปลี่ยนคลาสของคุณเพื่อให้มีฟังก์ชันบางประเภท หากคำอธิบายของฟังก์ชั่นนั้นเกินหัวคุณอาจอยู่ในสถานการณ์ที่ฉันตั้งใจจะอยู่ โปรดทราบว่านี่เป็นวิธีที่จะบรรลุเป้าหมาย ไม่ใช่วิธีเดียวและอาจมีวิธีที่ดีกว่าในสถานการณ์เฉพาะของคุณ มาเรียกชั้นเรียนAกัน destructor ของคุณถูกประกาศ (ในคำจำกัดความของคุณในชั้นเรียน) เป็นทั้ง

virtual ~A() = default;

หรือ

virtual ~A() {}

? ถ้าเป็นเช่นนั้นสองขั้นตอนจะเปลี่ยน destructor ของคุณเป็นประเภทของฟังก์ชั่นที่เราต้องการ ก่อนอื่นให้เปลี่ยนบรรทัดนั้นเป็น

virtual ~A();

ขั้นที่สองวางบรรทัดต่อไปนี้ในไฟล์ต้นฉบับที่เป็นส่วนหนึ่งของโครงการของคุณ (โดยเฉพาะไฟล์ที่มีการใช้คลาสหากคุณมี)

A::~A() {}

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


โอ้ไชโย! สำหรับคำอธิบายที่ละเอียดและมีขอบเขตที่ดีมาก
David C. Rankin

ต้องเลื่อนลงไปทางไกลเพื่ออ่านนี้ มี upvote สำหรับคำอธิบายที่ยอดเยี่ยม!
โทมัส

24

มีการคาดเดามากมายเกิดขึ้นในคำตอบต่าง ๆ ที่นี่ ฉันจะให้โค้ดด้านล่างที่มีขนาดเล็กที่สุดที่ทำซ้ำข้อผิดพลาดนี้และอธิบายว่าทำไมจึงเกิดขึ้น

รหัสที่น้อยที่สุดในการทำซ้ำข้อผิดพลาดนี้

IBase.hpp

#pragma once

class IBase {
    public:
        virtual void action() = 0;
};

Derived.hpp

#pragma once

#include "IBase.hpp"

class Derived : public IBase {
    public:
        Derived(int a);
        void action() override;
};

Derived.cpp

#include "Derived.hpp"
Derived::Derived(int a) { }
void Derived::action() {}

MyClass.cpp

#include <memory>
#include "Derived.hpp"

class MyClass {

    public:
        MyClass(std::shared_ptr<Derived> newInstance) : instance(newInstance) {

        }

        void doSomething() {
            instance->action();
        }

    private:
        std::shared_ptr<Derived> instance;
};

int main(int argc, char** argv) {
    Derived myInstance(5);
    MyClass c(std::make_shared<Derived>(myInstance));
    c.doSomething();
    return 0;
}

คุณสามารถรวบรวมโดยใช้ GCC ดังนี้:

g++ -std=c++11 -o a.out myclass.cpp Derived.cpp

ตอนนี้คุณสามารถทำซ้ำข้อผิดพลาดโดยการลบ = 0ใน IBase.hpp ฉันได้รับข้อผิดพลาดนี้:

~/.../catkin_ws$ g++ -std=c++11 -o /tmp/m.out /tmp/myclass.cpp /tmp/Derived.cpp
/tmp/cclLscB9.o: In function `IBase::IBase(IBase const&)':
myclass.cpp:(.text._ZN5IBaseC2ERKS_[_ZN5IBaseC5ERKS_]+0x13): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o: In function `IBase::IBase()':
Derived.cpp:(.text._ZN5IBaseC2Ev[_ZN5IBaseC5Ev]+0xf): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o:(.rodata._ZTI7Derived[_ZTI7Derived]+0x10): undefined reference to `typeinfo for IBase'
collect2: error: ld returned 1 exit status

คำอธิบาย

โปรดสังเกตว่ารหัสข้างต้นไม่จำเป็นต้องมีตัวทำลายเสมือนตัวสร้างหรือไฟล์พิเศษอื่น ๆ เพื่อให้การคอมไพล์สำเร็จ (แม้ว่าคุณควรมี)

วิธีทำความเข้าใจข้อผิดพลาดนี้มีดังนี้: Linker กำลังมองหา Constructor ของ IBase มันจะต้องใช้มันสำหรับผู้สร้างของ Derived อย่างไรก็ตามเนื่องจาก Derived จะแทนที่เมธอดจาก IBase แต่จะมี vtable ที่แนบมาซึ่งจะอ้างอิง IBase เมื่อ linker พูดว่า "การอ้างอิงที่ไม่ได้กำหนดไปยัง vtable สำหรับ IBase" โดยทั่วไปหมายความว่า Derived นั้นมีการอ้างอิง vtable ไปยัง IBase แต่ไม่สามารถค้นหารหัสวัตถุที่รวบรวมของ IBase เพื่อค้นหาได้ ดังนั้นบรรทัดล่างคือคลาส IBase มีการประกาศโดยไม่มีการใช้งาน นี่หมายถึงวิธีการใน IBase ถูกประกาศเป็นเสมือน แต่เราลืมที่จะทำเครื่องหมายเป็นเสมือนจริงหรือให้คำจำกัดความ

เคล็ดลับการพรากจากกัน

ถ้าอื่นทั้งหมดล้มเหลววิธีหนึ่งในการแก้ไขข้อผิดพลาดนี้คือการสร้างโปรแกรมขั้นต่ำที่รวบรวมและเปลี่ยนแปลงไปเรื่อย ๆ เพื่อให้เข้าสู่สถานะที่คุณต้องการ ในระหว่างนั้นให้รวบรวมเพื่อดูว่าเมื่อมันเริ่มล้มเหลว

หมายเหตุเกี่ยวกับระบบการสร้าง ROS และ Catkin

หากคุณกำลังรวบรวมชุดของคลาสใน ROS โดยใช้ระบบการสร้าง catkin คุณจะต้องมีบรรทัดต่อไปนี้ใน CMakeLists.txt:

add_executable(myclass src/myclass.cpp src/Derived.cpp)
add_dependencies(myclass theseus_myclass_cpp)
target_link_libraries(myclass ${catkin_LIBRARIES})

บรรทัดแรกโดยทั่วไปบอกว่าเราต้องการสร้างไฟล์ myclass ที่เรียกใช้งานได้และรหัสในการสร้างสามารถพบไฟล์ที่ตามมา หนึ่งในไฟล์เหล่านี้ควรมี main () โปรดสังเกตว่าคุณไม่จำเป็นต้องระบุไฟล์. hpp ใน CMakeLists.txt นอกจากนี้คุณไม่จำเป็นต้องระบุ Derived.cpp เป็นไลบรารี


18

ฉันเพิ่งพบสาเหตุอื่นสำหรับข้อผิดพลาดนี้ที่คุณสามารถตรวจสอบได้

คลาสพื้นฐานกำหนดฟังก์ชันเสมือนจริงเป็น:

virtual int foo(int x = 0);

และคลาสย่อยมี

int foo(int x) override;

ปัญหาคือการพิมพ์ผิดที่"=0"ควรจะอยู่นอกวงเล็บ:

virtual int foo(int x) = 0;

ดังนั้นในกรณีที่คุณเลื่อนลงมาคุณอาจไม่พบคำตอบ - นี่เป็นอย่างอื่นที่จะตรวจสอบ


12

สิ่งนี้สามารถเกิดขึ้นได้ค่อนข้างง่ายหากคุณลืมเชื่อมโยงไปยังไฟล์วัตถุที่มีคำจำกัดความ


1
โปรดเพิ่มคำอธิบายเพิ่มเติมลงในคำตอบของคุณและแก้ไขที่เป็นไปได้
Mohit Jain

1
การลืมลิงค์อาจรวมถึงการลืมเพิ่มในคำแนะนำการสร้าง ในกรณีของฉันไฟล์ cpp ของฉันมี 'กำหนด' อย่างสมบูรณ์แบบทุกอย่างยกเว้นฉันลืมเพิ่มไฟล์ cpp ลงในรายการแหล่งที่มา (ใน CMakeLists.txt ของฉัน แต่สิ่งเดียวกันสามารถเกิดขึ้นได้ในระบบบิลด์อื่นเช่นในไฟล์. pro) เป็นผลให้ทุกอย่างที่รวบรวมและแล้วฉันมีข้อผิดพลาดในเวลาที่การเชื่อมโยง ...
ปัญญาชน

@Mohit Jain วิธีการเชื่อมโยงในไฟล์วัตถุขึ้นอยู่กับการตั้งค่าสภาพแวดล้อมและการใช้เครื่องมือ น่าเสียดายที่การแก้ไขที่เฉพาะเจาะจงสำหรับคนคนหนึ่งอาจแตกต่างกันสำหรับคนอื่น (เช่น CMake กับเครื่องมือที่เป็นกรรมสิทธิ์เทียบกับ IDE เทียบกับ ฯลฯ )
Hazok

11

คอมไพเลอร์ GNU C ++ ต้องตัดสินใจว่าจะใส่ไว้ที่ไหนvtableในกรณีที่คุณมีคำจำกัดความของฟังก์ชั่นเสมือนจริงของวัตถุที่แพร่กระจายไปทั่วหน่วยการคอมไพล์หลาย ๆ ชุด (เช่นบางส่วนของคำนิยามฟังก์ชันวัตถุเสมือน ไฟล์ cpp และอื่น ๆ )

คอมไพเลอร์เลือกที่จะวางvtableในตำแหน่งเดียวกับที่มีการกำหนดฟังก์ชันเสมือนที่ประกาศไว้ครั้งแรก

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

เป็นผลข้างเคียงโปรดทราบว่าเฉพาะสำหรับฟังก์ชั่นเสมือนนี้โดยเฉพาะอย่างยิ่งที่คุณจะไม่ได้รับความผิดพลาดลิงเกอร์แบบดั้งเดิมเช่นคุณจะหายไปฟังก์ชั่น foo


8

ไม่ข้ามโพสต์ แต่ หากคุณกำลังจัดการกับการสืบทอด google hit อันที่สองคือสิ่งที่ฉันพลาดไปเช่น ควรกำหนดวิธีเสมือนทั้งหมด

เช่น:

virtual void fooBar() = 0;

ดู answare C ++ ไม่ได้อ้างอิงการอ้างอิงถึง vtable และการสืบทอดสำหรับรายละเอียด เพิ่งรู้ว่ามันถูกกล่าวถึงข้างต้น แต่ห่ามันอาจช่วยใครบางคน


8

ตกลงวิธีแก้ปัญหานี้คือคุณอาจพลาดคำจำกัดความ ดูตัวอย่างด้านล่างเพื่อหลีกเลี่ยงข้อผิดพลาดของคอมไพเลอร์ vtable:

// In the CGameModule.h

class CGameModule
{
public:
    CGameModule();
    ~CGameModule();

    virtual void init();
};

// In the CGameModule.cpp

#include "CGameModule.h"

CGameModule::CGameModule()
{

}

CGameModule::~CGameModule()
{

}

void CGameModule::init()    // Add the definition
{

}

7
  • คุณแน่ใจหรือว่าCDasherComponentมีตัวตนของผู้ทำลายหรือไม่ มันไม่ได้อยู่ที่นี่อย่างแน่นอน - คำถามคือถ้ามันอยู่ในไฟล์. cc
  • จากมุมมองของสไตล์CDasherModuleอย่างชัดเจนควรกำหนด virtualdestructor
  • ดูเหมือนว่าCGameModuleจะมีการเพิ่ม} ที่ส่วนท้าย (หลังจาก}; // for the class)
  • มีCGameModuleการเชื่อมโยงกับห้องสมุดที่กำหนดCDasherModuleและCDasherComponent?

- ใช่ CDasherComponent มีตัวทำลายใน cpp ฉันคิดว่ามันถูกประกาศใน. h เมื่อฉันโพสต์สิ่งนี้ - บันทึกรับรองสำเนาถูกต้อง. - นั่นคือวงเล็บพิเศษที่ฉันเพิ่มโดยไม่ได้ตั้งใจเมื่อลอกเอกสาร - เท่าที่ฉันเข้าใจใช่ ฉันกำลังแก้ไขไฟล์ automake ที่ฉันไม่ได้เขียน แต่ฉันได้ติดตามรูปแบบที่ใช้กับคลาสอื่นที่มีรูปแบบการสืบทอดเดียวกันจากคลาสเดียวกันดังนั้นหากฉันทำผิดพลาดไปหมด (เป็นไปได้ทั้งหมด) ฉันไม่คิดว่ามัน
RyanG

@RyanG: ลองย้ายนิยามฟังก์ชันเสมือนทั้งหมดลงในนิยามคลาส ตรวจสอบให้แน่ใจว่ามีทั้งหมดและดูว่าผลการเปลี่ยนแปลง
Stephen

5

บางทีสิ่งที่ขาดหายไปจาก destructor เสมือนจริงนั้นเป็นปัจจัยสนับสนุน?

virtual ~CDasherModule(){};

5

นี่เป็นผลการค้นหาครั้งแรกสำหรับฉันดังนั้นฉันคิดว่าฉันจะเพิ่มสิ่งอื่นเพื่อตรวจสอบ: ตรวจสอบให้แน่ใจว่าคำจำกัดความของฟังก์ชั่นเสมือนจริงอยู่ในชั้นเรียน ในกรณีของฉันฉันมีสิ่งนี้:

ไฟล์ส่วนหัว:

class A {
 public:
  virtual void foo() = 0;
};

class B : public A {
 public:
  void foo() override;
};

และในไฟล์. cc ของฉัน:

void foo() {
  ...
}

สิ่งนี้ควรอ่าน

void B::foo() {
}


3

มีคำตอบมากมายที่นี่ แต่ดูเหมือนว่าไม่มีใครที่จะแก้ปัญหาของฉันได้ ฉันมีดังต่อไปนี้:


class I {
    virtual void Foo()=0;
};

และในไฟล์อื่น (รวมอยู่ในการรวบรวมและการเชื่อมโยงแน่นอน)

class C : public I{
    void Foo() {
        //bar
    }
};

อย่างนี้ไม่ทำงานและฉันได้รับข้อผิดพลาดทุกคนกำลังพูดถึง เพื่อแก้ปัญหานี้ฉันต้องย้ายคำจำกัดความที่แท้จริงของ Foo ออกจากการประกาศในชั้นเรียนเช่น:

class C : public I{
    void Foo();
};

C::Foo(){
   //bar
}

ฉันไม่ใช่กูรู C ++ ดังนั้นฉันไม่สามารถอธิบายได้ว่าทำไมสิ่งนี้ถึงถูกต้องมากขึ้น แต่มันแก้ไขปัญหาให้ฉันได้


ฉันไม่ใช่กูรู C ++ แต่ดูเหมือนว่าจะเกี่ยวข้องกับการผสมการประกาศและคำนิยามในไฟล์เดียวกันกับไฟล์คำจำกัดความเพิ่มเติม
Terry G Lorber

1
เมื่อคำจำกัดความของฟังก์ชั่นอยู่ในคำจำกัดความของคลาสคุณจะได้รับการประกาศ "inline" โดยปริยาย ณ จุดนั้นฟังก์ชั่นเสมือนทั้งหมดของคุณจะถูกแทรก เมื่อคุณย้ายฟังก์ชั่นภายนอกคำจำกัดความของคลาสมันไม่ได้เป็นฟังก์ชั่น "อินไลน์" อีกต่อไป เนื่องจากคุณมีฟังก์ชั่นเสมือนที่ไม่มีการขีดเส้นใต้คอมไพเลอร์ของคุณจะรู้ตำแหน่งที่จะปล่อย vtable (คำอธิบายที่ละเอียดยิ่งขึ้นจะไม่เหมาะสมกับความคิดเห็น)
JaMiT

3

ดังนั้นฉันจึงใช้ Qt กับ Windows XP และคอมไพเลอร์ MinGW และสิ่งนี้ทำให้ฉันบ้า

โดยทั่วไป moc_xxx.cpp ถูกสร้างโดยไม่มีข้อมูลแม้ว่าฉันจะถูกเพิ่ม

Q_OBJECT

การลบทุกสิ่งที่ทำหน้าที่เสมือนชัดเจนและสิ่งที่คุณคาดเดาไม่ทำงาน ในที่สุดฉันก็เริ่มลบบรรทัดทีละบรรทัดและมันก็กลายเป็นว่าฉันมี

#ifdef something

รอบ ๆ ไฟล์ แม้เมื่อ #ifdef เป็นไฟล์ moc จริงไม่ได้ถูกสร้างขึ้น

ดังนั้นการลบ #ifdefs ทั้งหมดจึงช่วยแก้ไขปัญหาได้

สิ่งนี้ไม่ได้เกิดขึ้นกับ Windows และ VS 2013


แสดงความคิดเห็นออกบรรทัด Q_OBJECT g++ *.cpp ...ทำให้การสร้างแอปทดสอบอย่างง่ายของฉันกับธรรมดา (ต้องการบางสิ่งที่รวดเร็วและสกปรก แต่ qmake เต็มไปด้วยความโศกเศร้า)
Nathan Kidd

2

หากสิ่งอื่นล้มเหลวให้ค้นหาการทำซ้ำ ฉันถูกผิดโดยการอ้างอิงเริ่มต้นที่ชัดเจนเพื่อการก่อสร้างและ destructors จนกว่าฉันจะอ่านการอ้างอิงในการโพสต์อื่น มันเป็นวิธีการที่ค้างคาใด ๆ ในกรณีของฉันฉันคิดว่าฉันได้แทนที่การประกาศที่ใช้ char * xml เป็นพารามิเตอร์ด้วยอันที่หนึ่งโดยใช้ const char char * xml ที่ลำบากโดยไม่จำเป็น แต่แทนฉันได้สร้างขึ้นมาใหม่


2

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

เพื่อสรุปฉันจะบอกว่าถ้าคุณจ้องที่ชั้นเรียนนานพอและไม่เห็นว่าปัญหาด้านไวยากรณ์ที่เป็นไปได้อาจทำให้เกิดปัญหาได้ให้ค้นหาปัญหาการสร้างเช่นไฟล์ที่ขาดหายไปหรือไฟล์ซ้ำ


2

ในกรณีของฉันฉันกำลังใช้ Qt และได้กำหนดQObjectคลาสย่อยในไฟล์foo.cpp(ไม่ใช่.h) การแก้ไขคือการเพิ่มในตอนท้ายของ#include "foo.moc"foo.cpp


2

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

Foo.hpp:

class Foo
{
public:
    virtual void StartFooing();
};

Foo.cpp:

#include "Foo.hpp"

void Foo::StartFooing(){ //fooing }

รวบรวมด้วย:

g++ Foo.cpp -c

และ main.cpp:

#include "Foo.hpp"

int main()
{
    Foo foo;
}

รวบรวมและเชื่อมโยงกับ:

g++ main.cpp -o main

ให้ข้อผิดพลาดที่เราชื่นชอบ:

/tmp/cclKnW0g.o: ในฟังก์ชันmain': main.cpp:(.text+0x1a): undefined reference tovtable สำหรับ Foo 'collect2: ข้อผิดพลาด: ld ส่งคืนสถานะทางออก 1

สิ่งนี้เกิดขึ้นจากความไม่แน่นอนของฉันเนื่องจาก:

  1. Vtable ถูกสร้างขึ้นต่อชั้นเรียนในเวลารวบรวม

  2. Linker ไม่สามารถเข้าถึง vtable ที่อยู่ใน Foo.o


1

ฉันได้รับข้อผิดพลาดนี้ในสถานการณ์ต่อไปนี้

พิจารณากรณีที่คุณได้กำหนดการใช้งานฟังก์ชันสมาชิกของคลาสในไฟล์ส่วนหัวเอง ไฟล์ส่วนหัวนี้เป็นส่วนหัวที่ถูกส่งออก (ในคำอื่น ๆ มันอาจถูกคัดลอกไปยังบางส่วนที่พบบ่อย / รวมโดยตรงใน codebase ของคุณ) ตอนนี้คุณได้ตัดสินใจที่จะแยกการใช้งานฟังก์ชั่นสมาชิกเป็นไฟล์. cpp หลังจากที่คุณแยก / ย้ายการใช้งานเป็น. cpp ไฟล์ส่วนหัวจะมีเพียงต้นแบบของฟังก์ชันสมาชิกภายในคลาส หลังจากการเปลี่ยนแปลงข้างต้นถ้าคุณสร้าง codebase ของคุณคุณอาจได้รับข้อผิดพลาด "การอ้างอิงที่ไม่ได้กำหนดถึง 'vtable ... "

ในการแก้ไขปัญหานี้ก่อนสร้างต้องแน่ใจว่าคุณลบไฟล์ส่วนหัว (ซึ่งคุณทำการเปลี่ยนแปลง) ในไดเร็กทอรี common / include นอกจากนี้ตรวจสอบให้แน่ใจว่าคุณเปลี่ยน makefile ของคุณเพื่อรองรับ / เพิ่มไฟล์. o ใหม่ที่สร้างจากไฟล์. cpp ใหม่ที่คุณเพิ่งสร้างขึ้น เมื่อคุณทำตามขั้นตอนเหล่านี้คอมไพเลอร์ / ลิงเกอร์จะไม่บ่นอีกต่อไป


แปลก. หากส่วนหัวจะถูกคัดลอกที่อื่นระบบสร้างควรอัปเดตสำเนาโดยอัตโนมัติทันทีที่ต้นฉบับถูกแก้ไขและก่อนที่จะรวมในไฟล์อื่น หากคุณต้องทำด้วยตนเองคุณจะเมา
Offirmo

1

ฉันได้รับข้อผิดพลาดประเภทนี้ในสถานการณ์ที่ฉันพยายามเชื่อมโยงไปยังวัตถุเมื่อฉันได้รับข้อผิดพลาดที่ทำให้วัตถุถูกเพิ่มลงในที่เก็บถาวร

ว่าฉันมี libXYZ.a ที่ควรจะมี bioseq.o ใน int แต่มันไม่ได้

ฉันพบข้อผิดพลาด:

combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq'

นี่เป็นการเลิกที่แตกต่างจากที่กล่าวมาทั้งหมด ฉันจะเรียกวัตถุที่หายไปนี้ในปัญหาการเก็บถาวร


0

อาจเป็นไปได้ว่าคุณได้รับข้อความเช่น

SomeClassToTest.host.o: In function `class1::class1(std::string const&)':
class1.hpp:114: undefined reference to `vtable for class1'
SomeClassToTest.host.o: In function `class1::~class1()':
class1.hpp:119: undefined reference to `vtable for class1'
collect2: error: ld returned 1 exit status
[link] FAILED: 'g++' '-o' 'stage/tests/SomeClassToTest' 'object/tests/SomeClassToTest.host.o' 'object/tests/FakeClass1.SomeClassToTest.host.o'

หากคุณลืมกำหนดฟังก์ชั่นเสมือนจริงของคลาส FakeClass1 เมื่อคุณพยายามเชื่อมโยงการทดสอบหน่วยสำหรับคลาสอื่น SomeClass

//class declaration in class1.h
class class1
{
    public:
    class1()
    {
    }
    virtual ~class1()
    {
    }
    virtual void ForgottenFunc();
};

และ

//class definition in FakeClass1.h
//...
//void ForgottenFunc() {} is missing here

ในกรณีนี้ฉันขอแนะนำให้คุณตรวจสอบของปลอมสำหรับ class1 อีกครั้ง คุณอาจพบว่าคุณอาจลืมกำหนดฟังก์ชันเสมือนForgottenFuncในคลาสปลอมของคุณ


0

ฉันได้รับข้อผิดพลาดนี้เมื่อฉันเพิ่มคลาสที่สองให้กับคู่ของซอร์ส / ส่วนหัวที่มีอยู่ สองส่วนหัวของชั้นเรียนในไฟล์. h เดียวกันและคำจำกัดความของฟังก์ชั่นสำหรับสองชั้นในไฟล์. cpp เดียวกัน

ฉันเคยทำสิ่งนี้สำเร็จมาก่อนด้วยคลาสที่ตั้งใจทำงานใกล้ชิดกัน แต่ดูเหมือนว่าบางสิ่งบางอย่างไม่ชอบฉันในครั้งนี้ ยังไม่ทราบว่ามีอะไร แต่แยกออกเป็นหนึ่งคลาสต่อหนึ่งหน่วยการรวบรวม


ความพยายามที่ล้มเหลว:

_gui_icondata.h:

#ifndef ICONDATA_H
#define ICONDATA_H

class Data;
class QPixmap;

class IconData
{
public:
    explicit IconData();
    virtual ~IconData();

    virtual void setData(Data* newData);
    Data* getData() const;
    virtual const QPixmap* getPixmap() const = 0;

    void toggleSelected();
    void toggleMirror();
    virtual void updateSelection() = 0;
    virtual void updatePixmap(const QPixmap* pixmap) = 0;

protected:
    Data* myData;
};

//--------------------------------------------------------------------------------------------------

#include "_gui_icon.h"

class IconWithData : public Icon, public IconData
{
    Q_OBJECT
public:
    explicit IconWithData(QWidget* parent);
    virtual ~IconWithData();

    virtual const QPixmap* getPixmap() const;
    virtual void updateSelection();
    virtual void updatePixmap(const QPixmap* pixmap);

signals:

public slots:
};

#endif // ICONDATA_H

_gui_icondata.cpp:

#include "_gui_icondata.h"

#include "data.h"

IconData::IconData()
{
    myData = 0;
}

IconData::~IconData()
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    //don't need to clean up any more; this entire object is going away anyway
}

void IconData::setData(Data* newData)
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    myData = newData;
    if(myData)
    {
        myData->addIcon(this, false);
    }
    updateSelection();
}

Data* IconData::getData() const
{
    return myData;
}

void IconData::toggleSelected()
{
    if(!myData)
    {
        return;
    }

    myData->setSelected(!myData->getSelected());
    updateSelection();
}

void IconData::toggleMirror()
{
    if(!myData)
    {
        return;
    }

    myData->setMirrored(!myData->getMirrored());
    updateSelection();
}

//--------------------------------------------------------------------------------------------------

IconWithData::IconWithData(QWidget* parent) :
    Icon(parent), IconData()
{
}

IconWithData::~IconWithData()
{
}

const QPixmap* IconWithData::getPixmap() const
{
    return Icon::pixmap();
}

void IconWithData::updateSelection()
{
}

void IconWithData::updatePixmap(const QPixmap* pixmap)
{
    Icon::setPixmap(pixmap, true, true);
}

อีกครั้งเพิ่มแหล่งที่มาใหม่ / คู่หัวและตัด / วางชั้น IconWithData คำต่อคำใน "เพิ่งทำงาน"


0

กรณีของฉันเป็นคนโง่ฉันมีความผิดพลาดพิเศษ"หลังจากนั้น#includeและเดาว่าอะไร

undefined reference to vtable!

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

โดยพิเศษ"ฉันหมายถึง:

#include "SomeHeader.h""

0

ในกรณีของฉันฉันมีคลาสฐานชื่อว่า Person และคลาสที่ได้รับสองคลาสชื่อว่า Student และ Professor

วิธีที่โปรแกรมของฉันได้รับการแก้ไขคือ 1. ฉันทำทุกฟังก์ชั่นในคลาสฐานPure Virtual. 2 ฉันใช้destructors เสมือนทั้งหมดเป็นdefault ones.


-3

ฉันได้รับข้อผิดพลาดนี้เพียงเพราะชื่อของอาร์กิวเมนต์ตัวสร้างแตกต่างกันในไฟล์ส่วนหัวและในไฟล์การใช้งาน ตัวสร้างลายเซ็นคือ

PointSet (const PointSet & pset, Parent * parent = 0);

และสิ่งที่ฉันเขียนในการนำไปใช้เริ่มต้นด้วย

PointSet (const PointSet & pest, Parent * parent)

ดังนั้นฉันบังเอิญเปลี่ยน "pset" เป็น "pest" คอมไพเลอร์กำลังบ่นเกี่ยวกับตัวสร้างนี้หนึ่งและสองตัวอื่น ๆ ซึ่งไม่มีข้อผิดพลาดเลย ฉันใช้ g ++ เวอร์ชั่น 4.9.1 ภายใต้ Ubuntu และการกำหนด destructor เสมือนจริงในคลาสที่ได้รับนี้ทำให้ไม่แตกต่าง (ถูกกำหนดในคลาสพื้นฐาน) ฉันจะไม่เคยพบข้อผิดพลาดนี้ถ้าฉันไม่ได้วางเนื้อหาของตัวสร้างในไฟล์ส่วนหัวจึงกำหนดไว้ในระดับ


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