web-dev-qa-db-fra.com

Q_OBJECT lançant l'erreur 'référence non définie à vtable'

J'utilise Qt Creator 2.0.1 avec Qt 4.7.0 (32 bits) sur Windows 7 Ultimate 32 bits.

Considérez le code suivant, qui est un minimum pour produire l'erreur:

class T : public QObject, public QGraphicsItem
{
    Q_OBJECT

public:
    T() {}

    QRectF      boundingRect() const {return QRectF();}
    void        Paint(QPainter *Painter, const QStyleOptionGraphicsItem *option,
                      QWidget *widget) {}
};

int main()
{
    T t;
    return 0;
}

Le fragment de code ci-dessus provoque les erreurs de l'éditeur de liens suivantes:

Dans la fonction "T":

référence non définie à "vtable pour T"

référence non définie à "vtable pour T"

Dans la fonction `~ T ':

référence non définie à "vtable pour T"

référence non définie à "vtable pour T"

Si je commente la ligne qui contient Q_OBJECT, il compile très bien. J'ai besoin de signal et de slots avec QGraphicsItem donc j'ai besoin de Q_OBJECT.

Quel est le problème avec le code? Merci.

61
Donotalo

C'est parce que l'unité générée par MOC n'est pas incluse dans le processus de liaison. Ou peut-être que ce n'est pas du tout généré. La première chose que je ferais est de mettre la déclaration de classe dans un fichier d'en-tête séparé, peut-être que le système de construction n'analyse pas les fichiers d'implémentation.

Une autre possibilité est qu'une fois la classe en question n'appartenait pas au système de méta-objet Qt (c'est-à-dire qu'elle n'avait pas Q_OBJECT ou qu'elle n'héritait peut-être pas du tout de QObject), donc qmake doit être réexécuté pour créer le règles nécessaires pour le MOC. Le moyen le plus simple de forcer l'exécution de qmake consiste à apporter des modifications insignifiantes au fichier de projet pour mettre à jour son horodatage, comme l'ajout puis la suppression d'un espace blanc. Ou, si vous utilisez Qt Creator, sélectionnez simplement "Exécuter qmake" dans le menu contextuel du projet.

121
Sergei Tachenov

Si vous souhaitez définir une sous-classe QObject dans un fichier source, vous devez ajouter la ligne

#include "file.moc"

à un moment donné après la définition de votre classe où le nom du fichier source était file.cpp. Vous devrez bien sûr réexécuter qmake pour que la règle appropriée pour exécuter moc soit ajoutée au Makefile.

Ce n'est que dans un fichier d'en-tête que la présence de Q_OBJECT dans une définition de classe provoque l'appel de moc. S'il s'agit d'un fichier source, vous avez besoin de cette ligne supplémentaire pour forcer l'utilisation de moc.

Je suis sûr qu'une question similaire a déjà été posée, mais je ne l'ai pas trouvée.

23
Troubadour

Mettez vos classes Q_OBJECT dans des fichiers séparés. C'est un .h et un .cpp pour chaque classe. Les macros de méta-objets de Qt sont un peu difficiles à cet égard.

En outre, vous pouvez utiliser QGraphicsObject pour votre objectif. Vous fait gagner du temps là-bas.

Edit: je vois que vous utilisez Creator. Utilisez sa fonction Nouvelle classe C++ dans Nouveau fichier ou projet pour créer le fichier de la "bonne façon" :)

8
Stephen Chu

Voici du code de travail ajouté avec tous les correctifs fournis dans d'autres questions (compilation propre et aide à ces correctifs):

#include <QGraphicsItem>

class T : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem) //Required.

public:
    T() {}
    QRectF      boundingRect() const {return QRectF();}
    void        Paint(QPainter *Painter, const QStyleOptionGraphicsItem *option,
                      QWidget *widget) {}
};

int main(int argc, char *argv[])
{
    T *t = new T;
    return 0;
}

#include "main.moc" // Required.

Donc, crédit réel à Troubadour et serge_gubenko

6
Tuukka Lindroos

il y a deux ou trois choses à regarder:

  1. Ajoutez QT + = gui dans votre fichier pro
  2. Assurez-vous de définir vos classes dérivées de QObject dans vos fichiers d'en-tête uniquement (modifier: comme l'a noté Troubadour, cela n'est pas obligatoire)
  3. Ajoutez Q_INTERFACES (QGraphicsItem) à la déclaration de votre classe T

ci-dessous est un exemple:

t.h:

class T : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem)

public:
    T();
    QRectF boundingRect() const;
    void Paint(QPainter *Painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};

t.cpp:

T::T() {}

QRectF T::boundingRect() const
{
    return QRectF();
}

void T::Paint(QPainter *Painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(Painter);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

J'ai essayé de compiler le code ci-dessus et je n'ai eu aucun problème avec.

j'espère que cela vous aide,

4
serge_gubenko