72 votes

Erreurs du GCC C++ Linker : Référence indéfinie à 'vtable for XXX', Référence indéfinie à 'ClassName::ClassName()'.

Je suis en train de mettre en place un projet C++, sur Ubuntu x64, en utilisant Eclipse-CDT. Je fais essentiellement un "hello world" et un lien vers une bibliothèque commerciale tierce.

J'ai inclus les fichiers d'en-tête, lié à leurs bibliothèques, mais j'obtiens toujours des erreurs de liaison. Y a-t-il d'autres problèmes possibles que ceux qui sont évidents (par exemple, je suis sûr à 99 % que je me lie à la bonne bibliothèque) ?

  1. Existe-t-il un moyen de confirmer que les bibliothèques statiques que je lie sont 64 bits ?
  2. Existe-t-il un moyen de confirmer que la bibliothèque possède la classe (et les méthodes) que j'attends d'elle ?

dit Eclipse :

Building target: LinkProblem
Invoking: GCC C++ Linker
g++ -L/home/notroot/workspace/somelib-3/somelib/target/bin -o"LinkProblem"  ./src/LinkProblem.o   -lsomelib1 -lpthread -lsomelib2 -lsomelib3
./src/LinkProblem.o: In function \`main':
/home/notroot/workspace/LinkProblem/Debug/../src/LinkProblem.cpp:17: undefined reference to \`SomeClass::close()'
./src/LinkProblem.o: In function \`SomeOtherClass':
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148: undefined reference to \`SomeClass::SomeClass()'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148: undefined reference to \`vtable for SomeOtherClass'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:151: undefined reference to \`SomeClass::~SomeClass()'
./src/LinkProblem.o: In function \`~SomeOtherClass':
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefined reference to \`vtable for SomeOtherClass'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefined reference to \`SomeClass::~SomeClass()'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefined reference to \`SomeClass::~SomeClass()'
collect2: ld returned 1 exit status
make: \*\*\* \[LinkProblem\] Error 1

0 votes

La bibliothèque du tiers est-elle 64 bits ?

0 votes

Oui, il est en 64 bits. Mais vous pourriez être sur quelque chose. Comment puis-je m'assurer que mon code/projet est en 64 bits ? Dans Visual Studio, j'ai créé une configuration de construction x64.

1 votes

Existe-t-il un moyen de confirmer que la bibliothèque tierce est en 64 bits ? Par exemple, inspecter les fichiers .a avec un outil ou autre ?

168voto

mgiuca Points 10265

Cette erreur de liaison signifie généralement (d'après mon expérience) que vous avez surchargé une fonction virtuelle dans une classe enfant avec une déclaration, mais que vous n'avez pas donné de définition pour la méthode. Par exemple :

class Base
{
    virtual void f() = 0;
}
class Derived : public Base
{
    void f();
}

Mais vous n'avez pas donné la définition de f. Lorsque vous utilisez la classe, vous obtenez l'erreur du linker. Comme une erreur de liaison normale, c'est parce que le compilateur savait de quoi vous parliez, mais l'éditeur de liens ne pouvait pas trouver la définition. Il y a juste un message très difficile à comprendre.

4 votes

Merci. Je n'ai pas trouvé de solution pendant 2 heures.

2 votes

Exactement le problème que j'avais. Merci, vous m'avez épargné du temps et des efforts.

0 votes

Il est intéressant de noter que dans mon cas, cela ne se produit qu'avec les "virtuels purs" ! Le message d'erreur est vraiment trompeur.

74voto

Loki Astari Points 116129

En supposant que ces méthodes se trouvent dans l'une des librairies, cela ressemble à un problème d'ordonnancement.

Lorsque l'on lie des bibliothèques à un exécutable, on le fait dans l'ordre où elles sont déclarées.
De plus, l'éditeur de liens ne prendra que les méthodes/fonctions nécessaires pour résoudre les dépendances en cours. Si une bibliothèque ultérieure utilise des méthodes/fonctions qui n'étaient pas initialement requises par les objets, vous aurez des dépendances manquantes.

Comment cela fonctionne :

  • Prenez tous les fichiers objets et combinez-les en un exécutable.
  • Résoudre les éventuelles dépendances entre les fichiers d'objets.
  • Pour chaque bibliothèque dans l'ordre :
    • Vérifier les dépendances non résolues et voir si la librairie les résout.
    • Si oui, chargez la partie requise dans l'exécutable.

Exemple :

Les objets nécessitent :

  • Ouvrir
  • Fermer
  • BatchRead
  • BatchWrite

La Lib 1 prévoit :

  • Ouvrir
  • Fermer
  • lire
  • écrire

La Lib 2 prévoit

  • BatchRead (mais utilise lib1:read)
  • BatchWrite (mais utilise lib1:write)

Si c'est lié comme ça :

gcc -o plop plop.o -l1 -l2

Alors l'éditeur de liens échouera à résoudre les symboles de lecture et d'écriture.

Mais si je lie l'application comme ceci :

gcc -o plop plop.o -l2 -l1

Le lien sera alors correct. Comme l2 résout les dépendances BatchRead et BatchWrite mais en ajoute également deux nouvelles (read et write). Lorsque nous établissons un lien avec l1, les quatre dépendances sont résolues.

0 votes

Faites-vous référence à l'ordre des fichiers lib dans la ligne de commande G++ ?

0 votes

Oui. :-)

0 votes

Je remarque que votre exemple utilise "gcc" alors que ma question utilise "g++", dois-je utiliser "gcc" à la place ?

53voto

Rick Smith Points 1066

Qt C++ affiche cette erreur lorsque vous modifiez une classe de manière à ce qu'elle hérite de QObject (c'est-à-dire qu'elle peut maintenant utiliser les signaux/slots). L'exécution de qmake -r appellera moc et corrigera ce problème.

Si vous travaillez avec d'autres personnes via une sorte de contrôle de version, vous voudrez apporter des modifications à votre fichier .pro (c'est-à-dire ajouter/supprimer une ligne vide). Quand tout le monde aura vos changements et lancera make, make verra que le fichier .pro a changé et lancera automatiquement qmake. Cela évitera à vos coéquipiers de répéter votre frustration.

0 votes

Merci ! Très difficile à trouver sans indices !

1 votes

De même, si vous oubliez d'ajouter la classe à la section HEADER du fichier *.pro, vous obtiendrez la même erreur. L'en-tête de la classe doit être HEADER pour que moc détecte Q_OBJECT.

14voto

phord Points 2458

Le problème pour moi s'est avéré être assez obscur. Ma classe ressemblait à ça :

//-----------------------------------------
// libbase.h
class base {
public:
   base() { }
   virtual ~base() { }

   virtual int foo() { return 0; }
};
//-----------------------------------------

//-----------------------------------------
// libbase.cpp
#include "libbase.h"
//-----------------------------------------

//-----------------------------------------
// main.h
class derived : public base {
public:
    virtual int foo() ;
};
//-----------------------------------------

//-----------------------------------------
// main.cpp
int main () {
    derived d;
}
//-----------------------------------------

Le problème est dans l'éditeur de liens. Mon fichier d'en-tête était placé dans une bibliothèque, mais toutes les fonctions virtuelles étaient déclarées "en ligne" dans la déclaration de la classe. Comme aucun code n'utilisait (encore) les fonctions virtuelles, le compilateur ou l'éditeur de liens a négligé de mettre en place les corps de fonctions réels. Il a également omis de créer la table virtuelle.

Dans mon code principal où j'ai dérivé de cette classe, le linker a essayé de connecter ma classe à la classe de base et à sa vtable. Mais la vtable avait été mise au rebut.

La solution consistait à déclarer au moins un des corps des fonctions virtuelles en dehors de la déclaration de la classe, comme ceci :

//-----------------------------------------
// libbase.h
class base {
public:
   base() { }
   virtual ~base() ;   //-- No longer declared 'inline'

   virtual int foo() { return 0; }
};
//-----------------------------------------

//-----------------------------------------
// libbase.cpp
#include "libbase.h"
base::~base() 
{
}
//-----------------------------------------

0 votes

Pour moi c'était la sous-classe et sa fonction virtuelle

0 votes

Vous avez oublié le point-virgule à la fin de la définition de la classe :)

9voto

mschachter Points 61

En ce qui concerne les problèmes avec Qt4, je n'ai pas pu utiliser l'option qmake moc mentionnée ci-dessus. Mais ce n'était pas le problème de toute façon. J'avais le code suivant dans la définition de la classe :

class ScreenWidget : public QGLWidget
{
   Q_OBJECT        // must include this if you use Qt signals/slots
...
};

J'ai dû supprimer la ligne "Q_OBJECT" parce que je n'avais pas de signaux ou de slots définis.

0 votes

Merci ! J'avais ce problème en utilisant g++ et cmake, avec un code qui se compilait bien dans Studio.

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