Référence indéfinie à vtable

Donc, je reçois l’infâme horrible

référence indéfinie à ‘vtable …

erreur pour le code suivant (la classe en question est CGameModule.) et je ne peux pas comprendre pour la vie quel est le problème. Au début, je pensais que c’était lié à oublier de donner un corps à une fonction virtuelle, mais pour autant que je sache, tout est là. La chaîne d’inheritance est un peu longue, mais voici le code source associé. Je ne suis pas sûr de savoir quelles autres informations je devrais fournir.

Note: Le constructeur est l’endroit où cette erreur se produit, semble-t-il.

Mon code:

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::ssortingng GetTypedTarget(); std::ssortingng 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::ssortingng m_sTargetSsortingng; size_t m_stCurrentSsortingngPos; CDasherModel *m_pModel; CDasherInterfaceBase *m_pInterface; }; 

Hérite de …

 class CDasherModule; typedef std::vector::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; }; 

Qui hérite de ….

 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::ssortingng GetSsortingngParameter(int iParameter) const; void SetSsortingngParameter(int iParameter, const std::ssortingng & sValue) const; ParameterType GetParameterType(int iParameter) const; std::ssortingng GetParameterName(int iParameter) const; protected: Dasher::CEventHandler *m_pEventHandler; CSettingsStore *m_pSettingsStore; }; /// @} #endif 

La FAQ de GCC comporte une entrée:

La solution consiste à s’assurer que toutes les méthodes virtuelles qui ne sont pas pures sont définies. Notez qu’un destructeur doit être défini même s’il est déclaré pure-virtual [class.dtor] / 7.

Pour ce que cela vaut, oublier un corps sur un destructeur virtuel génère ce qui suit:

référence indéfinie à `vtable for CYourClass ‘.

J’ajoute une note car le message d’erreur est trompeur. (C’était avec la version 4.6.3 de gcc.)

Donc, j’ai compris le problème et c’était une combinaison de mauvaise logique et de ne pas être totalement familier avec le monde automake / autotools. J’ajoutais les fichiers corrects à mon modèle Makefile.am, mais je n’étais pas sûr de l’étape de notre processus de création qui créait le fichier Make lui-même. Donc, je compilais avec un vieux fichier makefile qui n’avait aucune idée de mes nouveaux fichiers.

Merci pour les réponses et le lien vers la FAQ de GCC. Je ne manquerai pas de lire cela pour éviter que ce problème ne se produise pour une raison réelle.

Si vous utilisez Qt, essayez de réexécuter qmake. Si cette erreur est dans la classe du widget, qmake pourrait ne pas avoir remarqué que la vtable de la classe de l’interface utilisateur devrait être régénérée. Cela a résolu le problème pour moi.

J’ai simplement eu cette erreur parce que mon fichier cpp n’était pas dans le fichier makefile.

La référence indéfinie à vtable peut également se produire en raison de la situation suivante. Essayez ceci:

Classe A Contient:

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

Classe B Contient:

  1. La définition de la fonction ci-dessusA.
  2. La définition de la fonctionB ci-dessus.

Classe C Contient: Maintenant, vous écrivez une classe C dans laquelle vous allez le dériver de la classe A.

Maintenant, si vous essayez de comstackr, vous obtiendrez une référence indéfinie à vtable pour la classe C en tant qu’erreur.

Raison:

functionA est défini en tant que virtuel pur et sa définition est fournie en classe B. functionB est défini comme virtuel (NOT PURE VIRTUAL) et essaie donc de trouver sa définition dans la classe A elle-même, mais vous en avez fourni la définition dans Class B.

Solution:

  1. Rendre la fonction B pure virtuelle (si vous avez des exigences comme ça) virtual void functionB(parameters) =0; (Cela fonctionne, il est testé)
  2. Fournir une définition pour functionB dans la classe A elle-même en la gardant virtuelle. (J’espère que ça marche comme je n’ai pas essayé ça)

Je viens de rencontrer une autre cause de cette erreur que vous pouvez vérifier.

La classe de base a défini une fonction virtuelle pure comme suit:

 virtual int foo(int x = 0); 

Et la sous-classe avait

 int foo(int x) override; 

Le problème était la faute de frappe que le "=0" était censé être en dehors de la parenthèse:

 virtual int foo(int x) = 0; 

Donc, si vous faites défiler cette liste, vous n’avez probablement pas trouvé la réponse – c’est autre chose à vérifier.

Le compilateur GNU C ++ doit décider où placer la vtable au cas où vous auriez la définition des fonctions virtuelles d’un object réparties sur plusieurs unités de compilation (par exemple, certaines définitions de fonctions virtuelles d’objects sont dans un fichier .cpp et d’autres dans un autre) fichier .cpp, etc.).

Le compilateur choisit de placer la vtable au même endroit que la première fonction virtuelle déclarée.

Maintenant, si pour une raison quelconque, vous avez oublié de fournir une définition pour cette première fonction virtuelle déclarée dans l’object (ou par erreur avez oublié d’append l’object compilé à la phase de liaison), vous obtiendrez cette erreur.

En tant qu’effet secondaire, veuillez noter que pour cette fonction virtuelle particulière, vous n’obtiendrez pas l’erreur de l’éditeur de liens traditionnelle, car il manque la fonction foo .

Cela peut arriver assez facilement si vous oubliez de créer un lien vers le fichier object qui a la définition.

C’est une erreur terrible car il semble que les gens aient supposé que peu de choses arbitraires étaient à l’origine de cette erreur. Par exemple, quelqu’un disait que vous deviez avoir un destructeur virtuel non pur dans la base alors que l’autre spéculait que ce destructeur devait se trouver dans son propre fichier cpp et encore un autre disant que l’ordre des fichiers d’inclusion était à l’origine de l’erreur! Ces causes ne semblent pas être vraies.

Si tout le rest échoue, une façon de déboguer cette erreur consiste à créer un programme minimal qui comstack, puis à le changer pour qu’il atteigne l’état souhaité. Entre-temps, continuez à comstackr pour voir quand il commence à tomber en panne.

Alors laissez-moi vous présenter un programme complet qui comstack bien, utilise des interfaces de style C ++ et possède ses propres fichiers cpp et header:

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  #include "Derived.hpp" class MyClass { public: MyClass(std::shared_ptr newInstance) : instance(newInstance) { } void doSomething() { instance->action(); } private: std::shared_ptr instance; }; int main(int argc, char** argv) { Derived myInstance(5); MyClass c(std::make_shared(myInstance)); c.doSomething(); return 0; } 

Vous pouvez comstackr ceci en utilisant GCC comme ceci:

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

Notez que cela ne nécessite pas de destructeurs virtuels, de constructeurs ou d’autres fichiers supplémentaires pour que la compilation réussisse (même si vous devez les avoir). Vous pouvez maintenant reproduire l’erreur en supprimant = 0 dans IBase.hpp. Je reçois cette erreur:

 ~/.../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 

La façon de comprendre cette erreur est la suivante: Linker recherche le constructeur de IBase. Ceci lui sera nécessaire pour le constructeur de Derived. Cependant, comme Derived remplace les méthodes d’IBase, il est associé à une vtable qui référencera IBase. Lorsque l’éditeur de liens dit “référence indéfinie à vtable pour IBase”, cela signifie essentiellement que Derived a une référence vtable à IBase, mais qu’il ne peut trouver aucun code d’object compilé d’IBase à rechercher. L’essentiel est que la classe IBase possède des déclarations sans implémentations. Cela signifie qu’une méthode dans IBase est déclarée comme virtuelle, mais nous avons oublié de la marquer comme étant virtuelle pure ou fournir sa définition.

Note sur le système de compilation ROS et Catkin

Si vous comstackz un ensemble de classes dans ROS en utilisant le système de compilation catkin, vous aurez besoin des lignes suivantes dans CMakeLists.txt:

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

La première ligne dit en gros que nous voulons créer un exécutable nommé myclass et que le code à comstackr peut être trouvé dans les fichiers suivants. L’un de ces fichiers devrait avoir main (). Notez que vous n’êtes pas obligé de spécifier des fichiers .hpp dans CMakeLists.txt. De plus, vous n’avez pas à spécifier Derived.cpp en tant que bibliothèque.

Ne pas traverser mais Si vous avez affaire à l’ inheritance, le deuxième hit de Google était ce que j’avais raté, à savoir. toutes les méthodes virtuelles doivent être définies.

Tel que:

 virtual void fooBar() = 0; 

Voir answare C ++ Référence indéfinie à vtable et inheritance pour plus de détails. Je viens de réaliser que c’est déjà mentionné ci-dessus, mais ça pourrait aider quelqu’un.

Ok, la solution est que vous avez peut-être manqué à la définition. Voir l’exemple ci-dessous pour éviter l’erreur du compilateur 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 { } 
  • Êtes-vous sûr que CDasherComponent a un corps pour le destructeur? Ce n’est certainement pas ici – la question est si elle est dans le fichier .cc.
  • Du sharepoint vue du style, CDasherModule devrait définir explicitement son destructeur virtual .
  • Il semble que CGameModule ait un extra } à la fin (après le }; // for the class ).
  • Est-ce que CGameModule est lié aux bibliothèques qui définissent CDasherModule et CDasherComponent ?

Peut-être que manquer le destructeur virtuel consortingbue au facteur?

 virtual ~CDasherModule(){}; 

C’était le premier résultat de recherche pour moi, alors j’ai pensé append une autre chose à vérifier: assurez-vous que la définition des fonctions virtuelles figure bien dans la classe. Dans mon cas, j’ai eu ceci:

header de fichier:

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

et dans mon fichier .cc:

 void foo() { ... } 

Cela devrait lire

 void B::foo() { } 

Pas peut-être Certainement ~CDasherModule() {} est manquant.

Tellement de réponses ici, mais aucune ne semblait avoir couvert mon problème. J’ai eu ce qui suit:


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

Et dans un autre fichier (inclus dans la compilation et la liaison, bien sûr)

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

Eh bien, cela n’a pas fonctionné et j’ai eu l’erreur dont tout le monde parle. Pour le résoudre, j’ai dû sortir la définition réelle de Foo de la déclaration de classe en tant que telle:

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

Je ne suis pas un gourou de C ++, donc je ne peux pas expliquer pourquoi c’est plus correct, mais cela a résolu le problème pour moi.

Donc, j’utilisais Qt avec Windows XP et le compilateur MinGW et cette chose me rendait fou.

Fondamentalement, moc_xxx.cpp a été généré vide même lorsque j’ai été ajouté

Q_OBJECT

Supprimer tout ce qui rend les fonctions virtuelles, explicites et ce que vous devinez ne fonctionne pas. Finalement, j’ai commencé à supprimer ligne par ligne et il s’est avéré que j’avais

 #ifdef something 

Autour du fichier Même si le #ifdef était vrai, le fichier moc n’était pas généré.

Donc, supprimer tous les #ifdefs a résolu le problème.

Cette chose ne se passait pas avec Windows et VS 2013.

J’ai cette erreur dans le scénario suivant

Considérons un cas où vous avez défini l’implémentation des fonctions membres d’une classe dans le fichier d’en-tête lui-même. Ce fichier d’en-tête est un en-tête exporté (en d’autres termes, il peut être copié dans un fichier commun / inclus directement dans votre base de code). Vous avez maintenant décidé de séparer l’implémentation des fonctions membres dans le fichier .cpp. Après avoir séparé / déplacé l’implémentation vers .cpp, le fichier d’en-tête n’a plus que les prototypes des fonctions membres à l’intérieur de la classe. Après les modifications ci-dessus, si vous générez votre base de code, vous pouvez obtenir l’erreur “référence indéfinie à” vtable … “.

Pour résoudre ce problème, avant de créer, assurez-vous de supprimer le fichier d’en-tête (auquel vous avez apporté des modifications) dans le répertoire common / include. Assurez-vous également de modifier votre fichier makefile pour accueillir / append le nouveau fichier .o créé à partir du nouveau fichier .cpp que vous venez de créer. Lorsque vous effectuez ces étapes, le compilateur / éditeur de liens ne se plaint plus.

Si tout échoue, recherchez les doublons. J’ai été mal orienté par la référence initiale explicite aux constructeurs et aux destructeurs jusqu’à ce que je lise une référence dans un autre article. C’est une méthode non résolue. Dans mon cas, je pensais avoir remplacé la déclaration qui utilisait char * xml comme paramètre par une autre utilisant le const char * xml inutilement gênant, mais au lieu de cela, j’avais créé un nouveau et laissé l’autre en place.

Il y a beaucoup de possibilités mentionnées pour causer cette erreur, et je suis sûr que beaucoup d’entre elles causent l’erreur. Dans mon cas, il y avait une autre définition de la même classe, due à une duplication du fichier source. Ce fichier a été compilé, mais pas lié, de sorte que l’éditeur de liens se plaignait de ne pouvoir le trouver.

Pour résumer, je dirais que si vous avez fixé la classe suffisamment longtemps et que vous ne parvenez pas à identifier le problème de syntaxe possible, recherchez des problèmes de génération tels qu’un fichier manquant ou un fichier dupliqué.

J’ai eu ce type d’erreur dans des situations où j’essayais de créer un lien vers un object lorsque j’ai reçu un bogue qui empêchait l’ajout de l’object à l’archive.

Disons que j’ai libXYZ.a supposé avoir bioseq.o dans int, mais ce n’est pas le cas.

J’ai une erreur:

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

Ceci est différent de tout ce qui précède. J’appellerais cet object manquant dans le problème de l’archive.

Dans mon cas, j’utilise Qt et j’ai défini une sous-classe QObject dans un foo.cpp (pas .h ). La solution consistait à append #include "foo.moc" à la fin de foo.cpp .

Il est également possible que vous receviez un message comme

 SomeClassToTest.host.o: In function `class1::class1(std::ssortingng 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' 

Si vous oubliez de définir une fonction virtuelle d’une classe FakeClass1 lorsque vous essayez de lier un test unitaire pour une autre classe SomeClass.

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

Et

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

Dans ce cas, je vous suggère de vérifier à nouveau votre faux pour class1. Vous constaterez probablement que vous avez peut-être oublié de définir une fonction virtuelle ForgottenFunc dans votre fausse classe.

J’ai eu cette erreur lorsque j’ai ajouté une deuxième classe à une paire source / en-tête existante. Deux en-têtes de classe dans le même fichier .h et des définitions de fonction pour deux classes dans le même fichier .cpp.

Je l’ai déjà fait avec succès, avec des classes qui sont censées travailler en étroite collaboration, mais apparemment, quelque chose ne m’a pas plu cette fois. Je ne sais toujours pas quoi, mais en les divisant en une seule classe par unité de compilation, tout est correct.


La tentative ratée:

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

Encore une fois, en ajoutant une nouvelle paire source / en-tête et en coupant / collant la classe IconWithData textuellement dans “juste travaillé”.

Je pense que cela vaut également la peine de mentionner que vous obtiendrez également le message lorsque vous essayez d’établir un lien avec un object de n’importe quelle classe possédant au moins une méthode virtuelle et que l’éditeur de liens ne trouve pas le fichier. Par exemple:

Foo.hpp:

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

Foo.cpp:

 #include "Foo.hpp" void Foo::StartFooing(){ //fooing } 

Compilé avec:

 g++ Foo.cpp -c 

Et main.cpp:

 #include "Foo.hpp" int main() { Foo foo; } 

Compilé et lié avec:

 g++ main.cpp -o main 

Donne notre erreur préférée:

/tmp/cclKnW0g.o: Dans la fonction main': main.cpp:(.text+0x1a): undefined reference to vtable pour Foo’ collect2: erreur: ld a renvoyé 1 état de sortie

Cet événement de ma conscience est dû à:

  1. Vtable est créé par classe au moment de la compilation

  2. Linker n’a pas access à vtable qui se trouve dans Foo.o

J’ai eu cette erreur simplement parce que le nom d’un argument de constructeur différait dans le fichier d’en-tête et dans le fichier d’implémentation. La signature du constructeur est

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

et ce que j’ai écrit dans la mise en œuvre a commencé avec

 PointSet (const PointSet & pest, Parent * parent) 

j’ai donc accidentellement remplacé “pset” par “pest”. Le compilateur se plaignait de celui-ci et de deux autres constructeurs dans lesquels il n’y avait aucune erreur. J’utilise la version 4.9.1 de g ++ sous Ubuntu. Et la définition d’un destructeur virtuel dans cette classe dérivée ne fait aucune différence (elle est définie dans la classe de base). Je n’aurais jamais trouvé ce bug si je ne collais pas les corps des constructeurs dans le fichier d’en-tête, les définissant ainsi en classe.