web-dev-qa-db-fra.com

Erreur Qt Linker: "référence indéfinie à vtable"

Voici mon en-tête:

#ifndef BARELYSOCKET_H
#define BARELYSOCKET_H

#include <QObject>
//! The First Draw of the BarelySocket!

class BarelySocket: public QObject
{
    Q_OBJECT

public:
    BarelySocket();
public slots:
    void sendMessage(Message aMessage);
signals:
    void reciveMessage(Message aMessage);

private:
    //   QVector<Message> reciveMessages;
};

#endif // BARELYSOCKET_H

Ceci est ma classe:

#include <QTGui>
#include <QObject>
#include "type.h"
#include "client.h"
#include "server.h"

#include "barelysocket.h"

BarelySocket::BarelySocket()
{
    //this->reciveMessages.clear();
    qDebug("BarelySocket::BarelySocket()");
}

void BarelySocket::sendMessage(Message aMessage)
{
}

void BarelySocket::reciveMessage(Message aMessage)
{
}

Je reçois une erreur de l'éditeur de liens: 

undefined reference to 'vtable for BarelySocket'
  • Cela implique que j'ai une méthode virtuelle non implémentée. Mais il n'y a pas de méthodes virtuelles dans ma classe.
  • J'ai commenté le vecteur en pensant que c'était la cause, mais l'erreur N'est pas partie.
  • La Message est une struct complexe, mais même en utilisant int à la place, le.
56
Thomas

Chaque fois que vous ajoutez un nouvel appel à la macro Q_OBJECT, vous devez réexécuter qmake. Le problème de vtables dont vous parlez est directement lié à cela.

Il suffit d’exécuter qmake et vous devriez être prêt à partir du principe que votre code ne pose aucun autre problème.

127
Michael

J'ai vu beaucoup de façons de résoudre le problème, mais aucune explication de pourquoi cela se produit, alors voilà.

Lorsque le compilateur voit une classe avec des fonctions virtuelles (directement déclarées ou héritées), il doit générer une vtable pour cette classe. Comme les classes sont généralement définies dans les en-têtes (et apparaissent donc dans plusieurs unités de traduction), la question est de savoir où placer la table vtable.

En général, le problème peut être résolu en générant la vtable dans chaque TU où la classe est définie, puis en laissant le lieur éliminer les doublons. Étant donné que les définitions de classe doivent être identiques à chaque occurrence de l'ODR, cela est sans danger. Cependant, cela ralentit également la compilation, alourdit les fichiers objets et oblige l'éditeur de liens à effectuer davantage de travail.

En guise d’optimisation, les compilateurs choisiront donc, dans la mesure du possible, un TU spécifique pour y placer la vtable. Dans l’ABI C++ commun, ce TU est celui où la fonction key fonction de la classe est implémentée dans, où la fonction clé est la première fonction membre virtuelle déclarée dans la classe, mais non définie.

Dans le cas des classes Qt, elles commencent généralement par la macro Q_OBJECT, et cette macro contient la déclaration

virtual const QMetaObject *metaObject() const;

qui, puisqu'il s'agit de la première fonction virtuelle de la macro, sera généralement la première fonction virtuelle de la classe et donc sa fonction clé. Le compilateur n'émettra donc pas la vtable dans la plupart des TU, mais uniquement celui qui implémente metaObject. Et l'implémentation de cette fonction est écrite automatiquement par moc quand elle traite l'en-tête. Donc, vous devez avoir moc traiter votre en-tête pour générer un nouveau fichier .cpp, puis inclure le fichier .cpp dans votre compilation.

Ainsi, lorsque vous avez un nouvel en-tête qui définit une classe dérivée de QObject, vous devez réexécuter qmake pour qu'il mette à jour vos makefiles afin d'exécuter moc sur le nouvel en-tête et compiler le fichier .cpp obtenu.

33
Sebastian Redl

J'ai rencontré cette erreur après avoir créé une petite classe dans un petit fichier "main.cpp" que j'avais créé pour tester quelque chose. 

Après avoir passé environ une heure à jouer, j’ai finalement transféré cette classe hors de main.cpp dans un fichier autonome hpp, mis à jour le fichier .pro (projet) et le projet s’est alors parfaitement bien construit. Ce n’était peut-être pas le problème ici, mais j’ai pensé que ce serait de toute façon une information utile.

13
David

Par expérience: souvent, un qmake && make clean && make help . Personnellement, je perçois que parfois la découverte du changement/la mise en cache des effets/ce que je ne sais pas xxxxx. Je ne peux pas dire pourquoi, mais c'est la première chose que je fais quand je rencontre ce genre d'erreur.

btw. il y a une faute de frappe à> recive <

Vous avez oublié d'appeler le constructeur QObject dans votre constructeur (dans la liste des initialiseurs). (Cela ne résout pas l'erreur cependant)

10
Ronny Brendel

En ce qui me concerne, j’ai remarqué lors de la construction de journaux que moc n’était pas appelé. Nettoyer tout n'a pas aidé. J'ai donc supprimé .pro.user, redémarré IDE et cela a fonctionné.

4
lstipakov

Lorsque vous dérivez une classe de QOBject (et utilisez la macro Q_OBJECT), n'oubliez pas de définir et de créer de manière spécifique les classes constructeur et destructeur. Il ne suffit pas d'utiliser les constructeurs/destructeurs par défaut du compilateur. Les conseils sur le nettoyage/l'exécution de qmake (et le nettoyage de vos fichiers moc_) sont toujours valables… .. Cela a résolu le même problème.

2
Pete

Les signaux ne doivent pas avoir d’implémentation (ceci sera généré par Qt). Supprimez l'implémentation reciveMessage de votre fichier .cpp. Cela peut résoudre votre problème.

Une autre chose que j'ai constatée: depuis que la classe BarelySocket hérite de QObject, elle doit avoir un destructeur virtuel pour éviter tout problème lors de la destruction. Cela doit être fait pour toutes les classes qui héritent d'une autre classe.

2
Patrice Bernassola

J'ai eu du mal avec cette heure d'erreur. Vous avez résolu le problème en plaçant les fichiers .cpp et .h dans un dossier séparé (!!) . Ajoutez ensuite le dossier du fichier .pro suivant: INCLUDEPATH + = $$ {_ PRO_FILE_PWD _} /../ MyClasses/CMyClassWidget

puis ajouté les fichiers .cpp et .h .

1
Sunny127

J'ai trouvé une autre raison pour laquelle vous pourriez voir ceci - puisque qmake analyse dans vos fichiers de classe si vous les avez modifiés d'une manière non standard, vous risquez d'obtenir cette erreur. Dans mon cas, j'avais un dialogue personnalisé qui héritait de QDialog, mais je voulais seulement que celui-ci soit compilé et exécuté lors de la création pour Linux, pas pour Windows ou OSX. Je viens de #ifdef __linux__ la classe afin qu'elle ne soit pas compilée, mais sous Linux, même si __linux__ était défini, il jetait qmake.

0
Preston