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
답변
GCC 자주 묻는 질문은 거기에 항목이 있습니다 :
해결책은 순수하지 않은 모든 가상 메소드가 정의되도록하는 것입니다. 소멸자는 순수 가상 [class.dtor] / 7로 선언 된 경우에도 정의되어야합니다.
답변
가상 소멸자의 몸을 잊어 버릴 경우 다음과 같은 결과가 발생합니다.
`vtable for CYourClass ‘에 대한 정의되지 않은 참조.
오류 메시지가 기만적이므로 메모를 추가하고 있습니다. (gcc 버전 4.6.3에서 사용되었습니다.)
답변
그래서 나는 문제를 알아 냈고 그것은 나쁜 논리의 조합이며 automake / autotools 세계에 완전히 익숙하지 않습니다. 올바른 파일을 Makefile.am 템플릿에 추가하고 있었지만 빌드 프로세스에서 어떤 단계가 실제로 makefile 자체를 생성했는지 확실하지 않았습니다. 그래서 새 파일에 대해 전혀 몰랐던 오래된 makefile로 컴파일하고있었습니다.
답변과 GCC FAQ 링크에 감사드립니다. 나는이 문제가 실제로 발생하는 것을 피하기 위해 그것을 읽을 것입니다.
답변
Qt를 사용하는 경우 qmake를 다시 실행하십시오. 이 오류가 위젯의 클래스에있는 경우 qmake가 ui 클래스 vtable을 다시 생성해야한다는 것을 알지 못했을 수 있습니다. 이것은 나를 위해 문제를 해결했습니다.
답변
다음 상황으로 인해 vtable에 대한 정의되지 않은 참조가 발생할 수도 있습니다. 이것을 시도하십시오 :
클래스 A는 다음을 포함합니다 :
virtual void functionA(parameters)=0;
virtual void functionB(parameters);
클래스 B에는 다음이 포함됩니다.
- 위의 기능에 대한 정의
- 위의 함수 B에 대한 정의.
클래스 C 포함 : 이제 클래스 A에서 클래스 C를 파생시킬 클래스 C를 작성 중입니다.
이제 컴파일하려고하면 클래스 C의 vtable에 대한 정의되지 않은 참조가 오류로 표시됩니다.
이유:
functionA
순수 가상으로 정의되고 해당 정의가 클래스 B에 제공됩니다.
functionB
가상으로 정의되어 있지 (NOT PURE VIRTUAL) 클래스 A 자체에서 정의를 찾으려고하지만 정의를 클래스 B에 제공했습니다.
해결책:
- 함수 B를 순수 가상으로 작성하십시오 (필요한 경우)
virtual void functionB(parameters) =0;
(이 작동하면 테스트 됨) - 클래스 A 자체의 함수 B에 대한 정의를 가상으로 유지하십시오. (내가 이것을 시도하지 않았 으면 좋겠다)
답변
내 cpp 파일이 makefile에 없기 때문에이 오류가 발생했습니다.
답변
무엇입니까 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에 대한 정의되지 않은 참조”오류의 세 가지 주요 원인이 있습니다.
- 멤버 함수에 정의가 없습니다.
- 오브젝트 파일이 링크되지 않습니다.
- 모든 가상 함수에는 인라인 정의가 있습니다.
이러한 원인만으로는 스스로 오류를 일으킬 수 없습니다. 오히려 오류를 해결하기 위해 해결해야합니다. 이러한 상황 중 하나를 의도적으로 만들면이 오류가 발생할 것으로 예상하지 마십시오. 다른 요구 사항이 있습니다. 이러한 상황을 해결하면이 오류가 해결 될 것으로 예상하십시오.
(이 질문에 3 번이면 충분했을 것입니다.)
오류를 수정하는 방법?
건너 뛰는 사람들을 환영합니다! 🙂
- 클래스 정의를보십시오. 순수 가상이 아닌 ( ”
= 0
“아님) 정의를 제공하는 ( ”= default
“아님) 첫 번째 인라인이 아닌 가상 함수를 찾으십시오 .- 그러한 기능이 없다면 클래스를 수정 해보십시오. (오류가 해결되었을 수 있습니다.)
- 경고에 대한 필립 토마스의 답변 도 참조하십시오 .
- 해당 기능에 대한 정의를 찾으십시오. 누락 된 경우 추가하십시오! (오류가 해결되었을 수 있습니다.)
- 경고에 대한 RedSpikeyThing의 답변 도 참조하십시오 .
- 링크 명령을 확인하십시오. 해당 함수의 정의가있는 오브젝트 파일을 언급하지 않으면 수정하십시오! (오류가 해결되었을 수 있습니다.)
- 오류가 해결 될 때까지 각 가상 기능에 대해 2 단계와 3 단계를 반복 한 다음 각각의 비가 상 기능에 대해 2 단계와 3 단계를 반복하십시오. 여전히 멈춰 있으면 각 정적 데이터 멤버마다 반복하십시오.
예
다를 수 있습니다 할, 때로는 별도의 질문 속으로 분기 무엇의 세부 사항 (같은 정의되지 않은 참조 / 확인되지 않은 외부 기호 오류이며, 내가 그것을 어떻게 해결합니까 무엇? ). 하지만 새로운 프로그래머에게 혼란을 줄 수있는 특정 사례에서해야 할 일에 대한 예를 제공하겠습니다.
1 단계에서는 특정 유형의 기능을 갖도록 클래스 수정에 대해 설명합니다. 해당 기능에 대한 설명이 머리 위로 넘어 가면 내가 해결하려는 상황에 처했을 수 있습니다. 이것이 목표를 달성하는 방법이라는 것을 명심하십시오. 유일한 방법은 아니며 특정 상황에서 더 나은 방법을 쉽게 찾을 수 있습니다. 수업에 전화합시다 A
. 소멸자가 (클래스 정의에서) 다음 중 하나로 선언되어 있습니까?
virtual ~A() = default;
또는
virtual ~A() {}
? 그렇다면 두 단계로 소멸자를 원하는 함수 유형으로 변경할 수 있습니다. 먼저 해당 줄을
virtual ~A();
둘째, 프로젝트의 일부인 소스 파일 (바람직하게는 클래스 구현 파일이있는 경우)에 다음 행을 넣으십시오.
A::~A() {}
그러면 (가상) 소멸자가 인라인이 아닌 컴파일러로 생성되지 않습니다. (함수 정의에 헤더 주석 추가와 같은 코드 형식 스타일에 맞게 항목을 자유롭게 수정하십시오.)