Le modèle objet de Qt est basé sur les QObject, qui offrent notamment le système très utile de communication inter-classes via signal / slot. Gérer l’héritage avec cette architecture peut-être de prime-abord légèrement tricky ! J’ai notamment eu quelques soucis de connexion signal / slot avec des classes héritant d’une interface (classe virtual pure).
Mon architecture
Une interface IMyClass et deux classes l’implémentant MyClass et MyClassSimulation. J’ai en prime un patron « factory » pour faciliter l’instanciation de ces classes (et pour avoir plus de souplesse dans l’avenir) : MyClassFactory.
Je connecte alors la méthode « slot » de mon instance de IMyClass (qui est soit MyClass, soit MyClassSimulation en ait) :
connect(otherClass, SIGNAL(aSuperSignal()), myClassInstance, SLOT(myGreatMethod());
L’erreur typique
QObject::connect: Cannot connect OtherClass::aSuperSignal() to (null)::myGreatMethod()
Tout se passe comme si ma classe « IMyClass » n’était pas instanciée. Or, je sais qu’elle l’est ! En fait, la connexion signal / slot n’arrive pas à passer la barrière de l’héritage dans ce cas.
Encore une fois Stack Overflow m’a sauvé la mise avec cette question : Using Qt signals and slots with multiple inheritance.
La solution
La solution qui fonctionne est la suivante :
Add a QObject* asQObject() abstract method to MyInterface and implement it as { return this; } in all subclasses.
En somme : l’interface doit forcer l’existence d’un objet contenant l’instance réelle de la classe.
L’interface devient alors :
class IMyClass: public QObject { Q_OBJECT public: IMyClass(QObject * object); virtual ~IMyClass() {} virtual QObject *object() { return m_object; } public slots: virtual void myGreatMethod()=0; private: QObject *m_object; }
#include "IMyClass.h" IMyClass::IMyClass(QObject *object) : m_object(object) {}
Et un exemple typique d’implémentation de cette interface :
MyClass::MyClass() : IMyClass(this) { // My default constructor }
Et pour que tout fonctionne à merveille, il ne reste plus qu’à modifier la connexion signal / slot en conséquence :
connect(otherClass, SIGNAL(aSuperSignal()), myClassInstance.getObject(), SLOT(myGreatMethod());
Cela vous convient-il ? D’autres remarques ? Bon courage 😉