567 votes

Référence indéfinie à vtable

Lors de la construction de mon programme C++, j'obtiens le message d'erreur suivant

référence indéfinie à 'vtable...

Quelle est la cause de ce problème ? Comment puis-je le résoudre ?


Il se trouve que j'obtiens l'erreur pour le code suivant (la classe en question est CGameModule.) et je n'arrive pas à comprendre quel est le problème. Au début, je pensais que c'était lié à l'oubli de donner un corps à une fonction virtuelle, mais d'après ce que je comprends, tout est là. La chaîne d'héritage est un peu longue, mais voici le code source correspondant. Je ne suis pas sûr des autres informations que je devrais fournir.

Note : Le constructeur est l'endroit où l'erreur se produit, il semblerait.

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

Hérite de...

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

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::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

4 votes

J'ai complètement oublié que le message d'erreur spécifie une fonction. Il s'agit du constructeur, donc j'ai vu le nom de ma classe et je n'ai pas fait le lien. Donc, c'est le constructeur qui lance ce message. Je vais ajouter ce détail à mon message original.

4 votes

Si vous n'avez pas reconstruit les fichiers de votre projet après avoir effectué des modifications importantes (par ex. qmake -project et ensuite qmake ) pour générer un nouveau Makefile qui est une source probable d'erreur lors de l'utilisation de Qt.

4 votes

@DavidC.Rankin, un autre problème lié à Qt est que si le fichier avec Q_OBJECT est copié en externe, mais ne fait pas encore partie du fichier .pro, alors bien qu'il compile bien, il ne se lie pas. Nous devons ajouter ce .h/.cpp dans le fichier .pro pour être en mesure de qmake .

563voto

Alexandre Hamez Points 1537

El FAQ SUR LE CCG a une entrée sur elle :

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é pur-virtuel [class.dtor]/7.

Par conséquent, vous devez fournir une définition pour le destructeur virtuel :

virtual ~CDasherModule()
{ }

27 votes

nm -C CGameModule.o | grep CGameModule:: listera les méthodes qui sont définies, en supposant que toute l'implémentation de votre classe se trouve dans le fichier objet logique. Vous pouvez comparer cette liste avec ce qui est défini comme virtuel pour déterminer ce que vous avez manqué.

179 votes

FFS, pourquoi le compilateur ne vérifie-t-il pas cela et n'affiche-t-il pas un message d'erreur ?

35 votes

Évidemment, cela ne peut être découvert que par l'éditeur de liens, pas par le compilateur.

192voto

Dan Points 369

Pour ce que cela vaut, l'oubli d'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 gcc version 4.6.3).

30 votes

J'ai dû mettre explicitement le corps de mon destructeur virtuel vide dans le fichier de définition (*.cc). L'avoir dans l'en-tête me donnait toujours l'erreur.

6 votes

Notez qu'une fois que j'ai ajouté le destructeur virtuel au fichier d'implémentation, gcc m'a indiqué l'erreur réelle, qui était un corps manquant sur une autre fonction.

2 votes

@PopcornKing J'ai vu le même problème. Même en définissant ~Destructor = default; dans le fichier d'en-tête n'a pas aidé. Y a-t-il un bogue documenté contre gcc ?

73voto

RyanG Points 572

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

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

62 votes

En bref : le .cpp n'a pas été inclus dans la compilation. Le message d'erreur est vraiment trompeur.

83 votes

Pour les utilisateurs de Qt : vous pouvez obtenir cette même erreur si vous oubliez de moc un en-tête.

9 votes

Je pense que vous devriez cependant accepter la réponse d'Alexandre Hamez. Les personnes qui recherchent cette erreur auront probablement besoin de sa solution plutôt que de la vôtre.

62voto

Srinivas Points 126

Une référence indéfinie à vtable peut également se produire dans les cas suivants. Essayez ceci :

La classe A contient :

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

La classe B contient :

  1. La définition de la fonctionA ci-dessus.
  2. La définition de la fonction ci-dessusB.

La classe C contient : Vous écrivez maintenant une classe C dans laquelle vous allez dériver de la classe A.

Maintenant, si vous essayez de compiler, vous obtiendrez l'erreur "Undefined reference to vtable for Class C".

Raison :

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

Solution :

  1. Faites de la fonction B une fonction virtuelle pure (si vous avez une telle exigence). virtual void functionB(parameters) =0; (Cela fonctionne, c'est testé)
  2. Fournir la définition de la fonction B dans la classe A elle-même en la gardant comme virtuelle. (J'espère que cela fonctionne car je n'ai pas essayé).

0 votes

@ilya1725 Votre modification suggérée Il ne s'agit pas seulement de corriger le formatage et d'autres éléments similaires, mais aussi de modifier la réponse, par exemple en disant que la classe C est dérivée de B au lieu de A, et vous modifiez la deuxième solution. Cela change substantiellement la réponse. Dans ces cas, veuillez plutôt laisser un commentaire à l'auteur. Nous vous remercions !

0 votes

@FabioTurati De quelle classe la classeC hérite-t-elle alors ? La phrase n'est pas claire. De plus, quelle est la signification de "Class C Contains" : " ?

0 votes

@ilya1725 Cette réponse n'est pas très claire, et je ne suis pas contre la modifier et l'améliorer. Ce que je dis, c'est que votre modification change le sens de la réponse, et c'est un changement trop radical. J'espère que l'auteur interviendra pour clarifier ce qu'il voulait dire (bien qu'il soit inactif depuis longtemps).

58voto

Will Points 301

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

En général, si vous oubliez de compiler ou de lier le fichier objet spécifique contenant la définition, vous rencontrerez cette erreur.

0 votes

En effet, il semble que le message change légèrement de l'habituel undefined reference to {function/class/struct} quand il y a virtual les choses impliquées. Ça m'a déstabilisé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X