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 관련 문제는 with 파일 Q_OBJECT이 외부로 복사되었지만 .pro 파일의 일부가 아닌 경우 컴파일이 잘되지만 링크되지 않는다는 것입니다. 이 .h/.cpp파일을 .pro 파일 에 추가해야 합니다 qmake.
iammilind

답변:


420

GCC 자주 묻는 질문은 거기에 항목이 있습니다 :

해결책은 순수하지 않은 모든 가상 메소드가 정의되도록하는 것입니다. 소멸자는 순수 가상 [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 for objfilename '에 대한 정의되지 않은 참조
Kemin Zhou

162

가상 소멸자의 몸을 잊어 버릴 경우 다음과 같은 결과가 발생합니다.

`vtable for CYourClass '에 대한 정의되지 않은 참조.

오류 메시지가 기만적이므로 메모를 추가하고 있습니다. (gcc 버전 4.6.3에서 사용되었습니다.)


23
빈 가상 소멸자의 본문을 정의 파일 (* .cc)에 명시 적으로 넣어야했습니다. 헤더에 여전히 오류가 발생했습니다.
PopcornKing

4
일단 가상 소멸자를 구현 파일에 추가하면 gcc는 실제 오류를 알려주었습니다.이 오류는 다른 함수에서 누락 된 본문이었습니다.
moodboom

1
@ PopcornKing 나는 같은 문제를 보았다. ~Destructor = default;헤더 파일에서 정의해도 도움이되지 않았습니다. gcc에 대해 문서화 된 버그가 있습니까?
RD

이것은 다른 문제 일 수 있지만 내 문제는 비가 상 소멸자를 구현하지 않은 것입니다 (고유 / 공유 포인터로 전환하여 소스 파일에서 제거했지만 헤더에 "구현"이 없었습니다) )
svenevs

이렇게하면 가상 소멸자에 빈 {} 본문을 추가하여 오류를 피할 수있어 내 문제가 해결되었습니다.
Bogdan Ionitza

56

그래서 나는 문제를 알아 냈고 그것은 나쁜 논리의 조합이며 automake / autotools 세계에 완전히 익숙하지 않습니다. 올바른 파일을 Makefile.am 템플릿에 추가하고 있었지만 빌드 프로세스에서 어떤 단계가 실제로 makefile 자체를 생성했는지 확실하지 않았습니다. 그래서 새 파일에 대해 전혀 몰랐던 오래된 makefile로 컴파일하고있었습니다.

답변과 GCC FAQ 링크에 감사드립니다. 나는이 문제가 실제로 발생하는 것을 피하기 위해 그것을 읽을 것입니다.


43
간단히 말하면 .cpp는 빌드에 포함되지 않았습니다. 오류 메시지는 실제로 오해의 소지가 있습니다.
Offirmo

67
Qt 사용자의 경우 : 헤더를 잊어 버린 경우에도 동일한 오류가 발생할 수 있습니다.
Chris Morlier

8
그래도 Alexandre Hamez의 대답을 받아 들여야한다고 생각합니다. 이 오류를 검색하는 사람들은 귀하의 솔루션 대신 자신의 솔루션이 필요할 것입니다.
Tim

13
-1 문제의 해결책 일 수도 있지만, 원래 질문에 대한 답변은 아닙니다. 정답은 단순히 필요한 기호가있는 객체 파일을 제공하지 않았다는 것입니다. 당신이 그것들을 제공하지 못한 이유는 또 다른 이야기입니다.
Walter

12
@ Walter : 사실 이것은 내가 찾던 정확한 대답이었습니다. 다른 것들은 분명하고 도움이되지 않습니다.
Edgar Bonet

50

Qt를 사용하는 경우 qmake를 다시 실행하십시오. 이 오류가 위젯의 클래스에있는 경우 qmake가 ui 클래스 vtable을 다시 생성해야한다는 것을 알지 못했을 수 있습니다. 이것은 나를 위해 문제를 해결했습니다.


2
방금 빌드 한 전체 폴더를 삭제했습니다.
Tomáš Zato-복원 모니카

이 고유하지 qmake, 내가 가진 동일한했다 cmake. 문제의 일부는 두 도구 모두 헤더 파일에 약간의 문제가 있기 때문에 필요할 때 항상 재 구축을 트리거하지는 않습니다.
MSalters

2
"Rebuild"reran qmake를 자동으로 생각했습니다. 귀하의 제안에 따라 "qmake를 실행"한 다음 "다시 빌드"를 수행하여 문제를 해결했습니다.
yano

45

다음 상황으로 인해 vtable에 대한 정의되지 않은 참조가 발생할 수도 있습니다. 이것을 시도하십시오 :

클래스 A는 다음을 포함합니다 :

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

클래스 B에는 다음이 포함됩니다.

  1. 위의 기능에 대한 정의
  2. 위의 함수 B에 대한 정의.

클래스 C 포함 : 이제 클래스 A에서 클래스 C를 파생시킬 클래스 C를 작성 중입니다.

이제 컴파일하려고하면 클래스 C의 vtable에 대한 정의되지 않은 참조가 오류로 표시됩니다.

이유:

functionA순수 가상으로 정의되고 해당 정의가 클래스 B에 제공됩니다. functionB가상으로 정의되어 있지 (NOT PURE VIRTUAL) 클래스 A 자체에서 정의를 찾으려고하지만 정의를 클래스 B에 제공했습니다.

해결책:

  1. 함수 B를 순수 가상으로 작성하십시오 (필요한 경우) virtual void functionB(parameters) =0; (이 작동하면 테스트 됨)
  2. 클래스 A 자체의 함수 B에 대한 정의를 가상으로 유지하십시오. (내가 이것을 시도하지 않았 으면 좋겠다)

@ ilya1725 제안 된 편집 은 서식 등을 수정하는 것이 아니라 대답을 변경하는 것입니다. 예를 들어 클래스 C가 A 대신 B에서 파생되었다고 말하고 두 번째 솔루션을 변경하고 있습니다. 이것은 실질적으로 답변을 변경합니다. 이 경우 저자에게 의견을 남겨주십시오. 감사합니다!
Fabio는 Reinstate Monica가

@ FabioTurati 그때부터 classC는 어떤 클래스를 상속합니까? 문장이 명확하지 않습니다. 또한 "Class C Contains :"의 의미는 무엇입니까?
ilya1725

@ ilya1725이 답변은 명확하지 않으며 편집하고 개선하는 데 반대하지 않습니다. 내가 말하는 것은 당신의 편집이 답의 의미를 바꾸고 너무나 극심한 변화라는 것입니다. 바라건대, 저자는 개입하여 자신이 의미하는 바를 명확히 할 것입니다 (오랫동안 활동하지 않았음에도 불구하고).
Fabio는 Reinstate Monica가

감사! 필자의 경우 계층 구조에 클래스가 2 개 밖에 없었습니다. 클래스 A는 순수한 가상 메소드를 선언했습니다. 클래스 B의 선언에 따르면이 메서드를 재정의한다고 명시했지만 아직 재정의 된 메서드 정의를 작성하지 않았습니다.
Nick Desaulniers

44

내 cpp 파일이 makefile에 없기 때문에이 오류가 발생했습니다.


실제로, 관련된 일 undefined reference to {function/class/struct}이있을 때 메시지가 평소와 약간 바뀌는 것 같습니다 virtual. 날 re 아
Keith M

30

무엇입니까 vtable?

문제를 해결하기 전에 오류 메시지에 대해 알고있는 것이 좋습니다. 높은 수준에서 시작한 다음 더 자세한 내용을 살펴 보겠습니다. 그렇게하면 사람들은 vtable에 대한 이해에 익숙해지면 미리 건너 뛸 수 있습니다. … 현재 많은 사람들이 건너 뛰고 있습니다. :) 고집하는 사람들을 위해 :

vtable은 기본적으로 C ++에서 가장 일반적인 다형성 구현입니다 . vtable을 사용할 때 모든 다형성 클래스는 프로그램 어딘가에 vtable을가집니다. 클래스 의 (숨겨진) 데이터 멤버 로 생각할 수 있습니다 . 다형성 클래스의 모든 객체는 가장 파생 된 클래스의 vtable과 연결됩니다. 이 연관성을 확인하면 프로그램이 다형성 마법을 사용할 수 있습니다. 중요한주의 사항 : vtable은 구현 세부 사항입니다. 대부분의 (모든?) C ++ 컴파일러가 vtable을 사용하여 다형성 동작을 구현하더라도 C ++ 표준에 의해 의무화되지는 않습니다. 내가 제시하는 세부 사항은 일반적이거나 합리적인 접근법입니다. 컴파일러는 이것으로부터 벗어날 수 있습니다!static

각 다형성 객체에는 객체의 가장 파생 된 클래스에 대한 vtable에 대한 (숨겨진) 포인터가 있습니다 (더 복잡한 경우에는 여러 개의 포인터가있을 수 있음). 포인터를 살펴보면 프로그램은 객체의 "실제"유형이 무엇인지 알 수 있습니다 (구성 중에는 제외하지만 특별한 경우는 건너 뛰겠습니다). 예를 들어, 유형의 객체 A가의 vtable을 가리 키지 않으면 A해당 객체는 실제로에서 파생 된 객체의 하위 객체입니다 A.

"vtable"이라는 이름은 " v irtual function table " 에서 유래 합니다 . (가상) 함수에 대한 포인터를 저장하는 테이블입니다. 컴파일러는 테이블 레이아웃 방법에 대한 규칙을 선택합니다. 간단한 방법은 클래스 정의 내에서 선언 된 순서대로 가상 함수를 거치는 것입니다. 가상 함수가 호출되면 프로그램은 vtable에 대한 객체의 포인터를 따르고 원하는 함수와 관련된 항목으로 이동 한 다음 저장된 함수 포인터를 사용하여 올바른 함수를 호출합니다. 이 작업을 수행하는 데 필요한 여러 가지 트릭이 있지만 여기서는 다루지 않겠습니다.

언제 어디서 vtable생성됩니까?

컴파일러는 vtable을 자동으로 생성합니다 (때로는 "emitted"라고 함). 컴파일러는 모든 변환 단위에서 다형성 클래스 정의를 보는 vtable을 생성 할 수 있지만 일반적으로 불필요한 오버 킬이 필요합니다. 대안은 ( gcc 및 아마도 다른 사람들이 사용) 클래스의 정적 데이터 멤버를 넣을 단일 소스 파일을 선택하는 방법과 유사하게 vtable을 배치 할 단일 변환 단위를 선택하는 것입니다. 이 선택 프로세스가 변환 단위를 선택하지 못하면 vtable은 정의되지 않은 참조가됩니다. 그러므로 그 메시지는 분명하게 명확하지 않은 오류입니다.

마찬가지로, 선택 프로세스가 변환 단위를 선택하지만 해당 오브젝트 파일이 링커에 제공되지 않으면 vtable은 정의되지 않은 참조가됩니다. 불행히도이 경우 선택 프로세스가 실패한 경우보다 오류 메시지가 명확하지 않을 수 있습니다. (이 가능성을 언급 한 답변자에게 감사합니다. 아마 그렇지 않으면 잊었을 것입니다.)

gcc가 사용하는 선택 프로세스는 구현하기 위해 하나의 소스 파일을 각 클래스에 전념하는 전통으로 시작하는 것이 합리적입니다. 해당 소스 파일을 컴파일 할 때 vtable을 내보내는 것이 좋습니다. 우리의 목표라고하겠습니다. 그러나이 전통을 따르지 않더라도 선발 과정이 필요합니다. 따라서 전체 클래스의 구현을 찾는 대신 클래스의 특정 멤버의 구현을 찾으십시오. 전통이 준수되고 해당 구성원이 실제로 구현 되면 목표가 달성됩니다.

gcc (및 잠재적으로 다른 컴파일러)에 의해 선택된 멤버는 순수 가상이 아닌 최초의 비 인라인 가상 함수입니다. 다른 멤버 함수보다 먼저 생성자와 소멸자를 선언하는 군중의 일부인 경우 해당 소멸자가 선택 될 가능성이 높습니다. (소멸자를 가상으로 만드는 것을 기억 했습니까?) 예외가 있습니다. 가장 일반적인 예외는 소멸자에 대해 인라인 정의가 제공 될 때와 기본 소멸자가 요청 될 때 ( " = default"사용) 예상됩니다 .

잘 알려진 사람은 다형성 클래스가 모든 가상 함수에 대해 인라인 정의를 제공 할 수 있음을 알 수 있습니다. 선택 과정이 실패하지 않습니까? 구형 컴파일러에서는 작동합니다. 최신 컴파일러 가이 상황을 해결했다고 읽었지만 관련 버전 번호를 모릅니다. 이것을 찾아 볼 수는 있지만 코드를 작성하거나 컴파일러가 불평하기를 기다리는 것이 더 쉽습니다.

요약하면 "vtable에 대한 정의되지 않은 참조"오류의 세 가지 주요 원인이 있습니다.

  1. 멤버 함수에 정의가 없습니다.
  2. 오브젝트 파일이 링크되지 않습니다.
  3. 모든 가상 함수에는 인라인 정의가 있습니다.

이러한 원인만으로는 스스로 오류를 일으킬 수 없습니다. 오히려 오류를 해결하기 위해 해결해야합니다. 이러한 상황 중 하나를 의도적으로 만들면이 오류가 발생할 것으로 예상하지 마십시오. 다른 요구 사항이 있습니다. 이러한 상황을 해결하면이 오류가 해결 될 것으로 예상하십시오.

(이 질문에 3 번이면 충분했을 것입니다.)

오류를 수정하는 방법?

건너 뛰는 사람들을 환영합니다! :)

  1. 클래스 정의를보십시오. 순수 가상이 아닌 ( " = 0"아님) 정의를 제공하는 ( " = default"아님) 첫 번째 인라인이 아닌 가상 함수를 찾으십시오 .
    • 그러한 기능이 없다면 클래스를 수정 해보십시오. (오류가 해결되었을 수 있습니다.)
    • 경고에 대한 필립 토마스의 답변 도 참조하십시오 .
  2. 해당 기능에 대한 정의를 찾으십시오. 누락 된 경우 추가하십시오! (오류가 해결되었을 수 있습니다.)
  3. 링크 명령을 확인하십시오. 해당 함수의 정의가있는 오브젝트 파일을 언급하지 않으면 수정하십시오! (오류가 해결되었을 수 있습니다.)
  4. 오류가 해결 될 때까지 각 가상 기능에 대해 2 단계와 3 단계를 반복 한 다음 각각의 비가 상 기능에 대해 2 단계와 3 단계를 반복하십시오. 여전히 멈춰 있으면 각 정적 데이터 멤버마다 반복하십시오.


다를 수 있습니다 할, 때로는 별도의 질문 속으로 분기 무엇의 세부 사항 (같은 정의되지 않은 참조 / 확인되지 않은 외부 기호 오류이며, 내가 그것을 어떻게 해결합니까 무엇? ). 하지만 새로운 프로그래머에게 혼란을 줄 수있는 특정 사례에서해야 할 일에 대한 예를 제공하겠습니다.

1 단계에서는 특정 유형의 기능을 갖도록 클래스 수정에 대해 설명합니다. 해당 기능에 대한 설명이 머리 위로 넘어 가면 내가 해결하려는 상황에 처했을 수 있습니다. 이것이 목표를 달성하는 방법이라는 것을 명심하십시오. 유일한 방법은 아니며 특정 상황에서 더 나은 방법을 쉽게 찾을 수 있습니다. 수업에 전화합시다 A. 소멸자가 (클래스 정의에서) 다음 중 하나로 선언되어 있습니까?

virtual ~A() = default;

또는

virtual ~A() {}

? 그렇다면 두 단계로 소멸자를 원하는 함수 유형으로 변경할 수 있습니다. 먼저 해당 줄을

virtual ~A();

둘째, 프로젝트의 일부인 소스 파일 (바람직하게는 클래스 구현 파일이있는 경우)에 다음 행을 넣으십시오.

A::~A() {}

그러면 (가상) 소멸자가 인라인이 아닌 컴파일러로 생성되지 않습니다. (함수 정의에 헤더 주석 추가와 같은 코드 형식 스타일에 맞게 항목을 자유롭게 수정하십시오.)


아, 브라보! 매우 상세하고 범위가 넓은 설명.
David C. Rankin

이것을 읽으려면 아래로 스크롤해야했습니다. 훌륭한 설명을위한 공감대를 가지십시오!
토마스

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

이제 = 0IBase.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

설명

위의 코드에는 컴파일에 성공하기 위해 가상 소멸자, 생성자 또는 기타 추가 파일이 필요하지 않습니다.

이 오류를 이해하는 방법은 다음과 같습니다. 링커가 IBase의 생성자를 찾고 있습니다. 이것은 Derived의 생성자에게 필요합니다. 그러나 파생은 IBase에서 메소드를 대체하므로 IBase를 참조하는 vtable이 첨부되어 있습니다. 링커에서 "IBase에 대한 vtable에 대한 정의되지 않은 참조"가 표시되면 기본적으로 Derived에 IBase에 대한 vtable 참조가 있지만 조회 할 IBase의 컴파일 된 객체 코드를 찾을 수 없음을 의미합니다. 결론적으로 IBase 클래스에는 구현이없는 선언이 있습니다. 이것은 IBase의 메소드가 가상으로 선언되었지만 순수 가상으로 표시하거나 정의를 제공하는 것을 잊었습니다.

이별 팁

다른 모든 방법이 실패하면이 오류를 디버깅하는 한 가지 방법은 컴파일하는 최소한의 프로그램을 작성한 다음 계속 변경하여 원하는 상태가되는 것입니다. 그 사이에 언제 컴파일이 실패하는지 확인하십시오.

ROS 및 Catkin 빌드 시스템에 대한 참고 사항

catkin 빌드 시스템을 사용하여 ROS에서 위 클래스 세트를 컴파일하는 경우 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 ()이 있어야합니다. CMakeLists.txt에 .hpp 파일을 지정할 필요가 없습니다. 또한 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 파일을 소스 목록에 추가하는 것을 잊어 버렸습니다 (CMakeLists.txt에서 .pro 파일과 같은 다른 빌드 시스템에서도 동일). 결과적으로 모든 것이 컴파일 된 후 링크 타임에 오류가 발생했습니다.
sage

@Mohit Jain 객체 파일을 링크하는 방법은 환경 설정 및 툴링에 따라 다릅니다. 불행히도 한 사람에 대한 특정 수정은 다른 사람에 대해 다를 수 있습니다 (예 : CMake vs 독점 도구 vs IDE vs 등)
Hazok

11

GNU C ++ 컴파일러는 vtable여러 컴파일 단위에 분산 된 객체의 가상 기능에 대한 정의가있는 경우 (예 : 일부 가상 기능 정의는 다른 객체의 .cpp 파일에 있음) 의사 결정 을해야합니다. cpp 파일 등).

컴파일러 vtable는 선언 된 첫 번째 가상 함수가 정의 된 위치와 동일한 곳에 배치 하도록 선택합니다 .

이제 어떤 이유로 오브젝트에 선언 된 첫 번째 가상 함수에 대한 정의를 제공하지 않은 경우 (또는 링크 단계에서 컴파일 된 오브젝트를 추가하지 않은 경우)이 오류가 발생합니다.

부작용으로,이 특정 가상 함수에 대해서만 함수 foo가 누락 된 것처럼 전통적인 링커 오류가 발생하지 않습니다 .



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소멸자를 명시 적으로 정의해야합니다 virtual.
  • 끝에 () 뒤에 CGameModule추가 항목이있는 것 같습니다 .}}; // for the class
  • 되어 CGameModule정의 라이브러리에 링크되는 CDasherModuleCDasherComponent?

예, CDasherComponent는 cpp에 소멸자 몸체를 가지고 있습니다. 나는 이것을 게시 할 때 .h에 선언되었다고 생각했습니다. - 정식 주목. -그것은 문서를 제거 할 때 실수로 추가 한 추가 브래킷이었습니다. 내가 이해하는 한 필자가 작성하지 않은 automake 파일을 수정했지만 동일한 클래스의 동일한 상속 패턴으로 다른 클래스에서 작동 한 패턴을 따르고 있습니다. 따라서 바보 같은 실수를하지 않은 한 그렇다고 생각하지 않습니다.
RyanG

@RyanG : 모든 가상 함수 정의를 클래스 정의로 이동하십시오. 그들이 모두 있는지 확인하고 결과가 변경되는지 확인하십시오.
Stephen

5

가상 소멸자가없는 것이 아마도 기여 요인입니까?

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
함수 정의가 클래스 정의 안에있을 때는 암시 적으로 "인라인"으로 선언되었습니다. 이때 모든 가상 기능이 인라인되었습니다. 함수를 클래스 정의 밖으로 옮기면 더 이상 "인라인"함수가 아닙니다. 인라인되지 않은 가상 함수가 있기 때문에 컴파일러는 vtable을 어디에서 방출해야 하는지를 알고있었습니다. (더 자세한 설명은 주석에 맞지 않을 것입니다.)
JaMiT

3

그래서 Windows XP 및 MinGW 컴파일러에서 Qt를 사용하고 있었고이 일로 나를 미치게했습니다.

기본적으로 moc_xxx.cpp는 추가 된 경우에도 비어 있습니다.

Q_OBJECT

함수를 가상적이고 명시 적으로 만드는 모든 것을 삭제하고 당신이 생각하지 않는 것은 무엇이든 작동하지 않습니다. 마지막으로 한 줄씩 제거하기 시작했고

#ifdef something

파일 주위. #ifdef가 true 일 때도 moc 파일이 생성되지 않았습니다.

따라서 #ifdef를 모두 제거하면 문제가 해결되었습니다.

이 문제는 Windows 및 VS 2013에서 발생하지 않았습니다.


Q_OBJECT 줄을 주석 처리하면 간단한 테스트 앱을 일반 빌드로 만들었습니다 g++ *.cpp .... (빠르고 더러운 것이 필요하지만 qmake는 슬픔으로 가득했습니다.)
Nathan Kidd

2

다른 방법으로 모두 실패하면 복제를 찾으십시오. 다른 게시물에서 참조를 읽을 때까지 생성자와 소멸자에 대한 명시 적 초기 참조에 의해 잘못 지시되었습니다. 그건 어떤 해결되지 않은 방법. 필자의 경우 char * xml을 매개 변수로 사용하는 선언을 불필요하게 번거로운 const char * xml을 사용하는 선언으로 대체했다고 생각했지만 대신 새 것을 작성하고 다른 것을 그대로 두었습니다.


2

이 오류를 일으킬 수있는 많은 가능성이 있으며 많은 사람들이 오류를 일으킬 것이라고 확신합니다. 필자의 경우 소스 파일이 중복되어 동일한 클래스에 대한 다른 정의 가있었습니다 . 이 파일은 컴파일되었지만 링크되지 않았으므로 링커에서 찾을 수 없다는 불평이있었습니다.

요약하자면, 클래스를 충분히 오래 쳐다보고 어떤 구문 문제로 인해 발생 가능한지 알 수 없다면 누락 된 파일 또는 복제 된 파일과 같은 빌드 문제를 찾으십시오.


2

제 경우에는 Qt를 사용 QObject하고 있으며 foo.cpp(not .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 toFoo 'collect2의 기능 vtable에서 : 오류 : ld가 1 개의 종료 상태를 리턴했습니다.

이것은 내 이해하지 못하기 때문에 발생합니다.

  1. 컴파일 타임에 클래스마다 Vtable이 생성됩니다.

  2. 링커가 Foo.o에있는 vtable에 액세스 할 수 없습니다


1

다음 시나리오 에서이 오류가 발생했습니다.

헤더 파일 자체에서 클래스의 멤버 함수 구현을 정의한 경우를 고려하십시오. 이 헤더 파일은 내 보낸 헤더입니다 (즉, 코드베이스에서 직접 일부 공통 / 포함에 복사 될 수 있음). 이제 멤버 함수의 구현을 .cpp 파일로 분리하기로 결정했습니다. 구현을 .cpp로 분리 / 이동 한 후에는 헤더 파일에 클래스 내부의 멤버 함수 프로토 타입 만 있습니다. 위의 변경 후 코드베이스를 빌드하면 " 'vtable ...에 대한 정의되지 않은 참조'오류가 발생할 수 있습니다.

이 문제를 해결하려면 빌드하기 전에 common / include 디렉토리에서 헤더 파일 (변경 한 파일)을 삭제해야합니다. 또한 방금 만든 새 .cpp 파일에서 작성된 새 .o 파일을 수용 / 추가하도록 makefile을 변경해야합니다. 이 단계를 수행하면 컴파일러 / 링커가 더 이상 불평하지 않습니다.


이상한. 헤더를 다른 곳에 복사하려면 빌드 시스템은 원본을 수정하자마자 다른 파일에 포함시키기 전에 사본을 자동으로 업데이트해야합니다. 수동으로 해야하는 경우 나사가 고정되어 있습니다.
Offirmo

1

객체가 아카이브에 추가되지 못하게하는 버그를 만들 때 객체에 연결하려고 할 때 이러한 유형의 오류가 발생했습니다.

int에 bioseq.o가 있어야하는 libXYZ.a가 있지만 그렇지 않다고 가정 해보십시오.

오류가 발생했습니다.

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'

다른 클래스 SomeClass에 대한 단위 테스트를 연결하려고 할 때 FakeClass1 클래스의 가상 함수를 정의하는 것을 잊어 버린 경우.

//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. 기본 클래스 2 에서 모든 기능 을 만들었습니다 Pure Virtual.. 모든 가상 소멸자를 다음 과 같이 사용 했습니다.default ones.


-3

헤더 파일과 구현 파일에서 생성자 인수의 이름이 다르기 때문에이 오류가 발생했습니다. 생성자 서명은

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

내가 구현에서 쓴 것은

PointSet (const PointSet & pest, Parent * parent)

따라서 우연히 "pset"을 "pest"로 교체했습니다. 컴파일러는 전혀 오류가 없었던이 하나와 두 개의 다른 생성자에 대해 불평했습니다. 우분투에서 g ++ 버전 4.9.1을 사용하고 있습니다. 이 파생 클래스에서 가상 소멸자를 정의해도 아무런 차이가 없습니다 (기본 클래스에 정의되어 있음). 헤더 파일에 생성자의 본문을 붙여 넣지 않으면이 버그를 찾지 못했을 것이므로 클래스 내에서 정의하십시오.


7
그것은 전혀 차이를 만들지 않을 것입니다. 다른 곳에서 오류가 발생하여 실수로 수정했을 것입니다.
MM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.