2011-07-23 9 views
6

Comme the QObject documentation et beaucoup d'autres expliquent, un QObject a une identité et cache ainsi son constructeur de copie et son opérateur d'affectation.Réflexion Qt C++ avec copie et affectation

Cependant, je ne dérive pas de QObject pour sa fonction de propriétés dynamiques ou la fonction signaux/slots. Je veux seulement réflexion, ou la possibilité d'accéder à Foo::staticMetaObject.

class Foo : public QObject { 
    Q_OBJECT 
    Q_ENUMS(Color) 
public: 
    enum Color { Blue, Red, Pink }; 
private: 
    Color color; 
}; 

Q_DECLARE_METATYPE(Foo::Color) 

Je ne peux donc pas copier Foo avec:

Foo a; 
Foo b; 
a = b; 

Quelle est la meilleure façon de permettre la copie et l'affectation dans ce cas? Ai-je absolument besoin d'écrire un constructeur de copie et un opérateur d'affectation? De quoi auraient-ils l'air? La réflexion fonctionnera-t-elle encore?

+0

Vous voulez dire que n'utilise pas 'QObject'. Parce que la réflexion n'est pas la source du problème ici. Le problème est que QObject rend la copie et l'assignation privées (pour une bonne raison). –

+1

@kristianp, bonne suggestion, je ferais cette édition * si * @alexisdm n'a pas fourni la réponse exacte à ma question exacte. Souvent, la réponse est "ne faites pas ce que vous demandez de faire" mais dans ce cas, même si c'est une réponse raisonnable, il y en a une qui me laisse faire ce que je veux faire: 'Q_GADGET' –

Répondre

14

Si vous êtes intéressés seulement par réflexion pour

  • le nom de classe,
  • énumérations et drapeaux (Q_ENUMS, Q_FLAGS),
  • info classe (Q_CLASSINFO),

vous pouvez utiliser Q_GADGET instead of Q_OBJECT:

class Foo { 
    Q_GADGET 
    Q_ENUMS(Color) 
public: 
    enum Color { Blue, Red, Pink }; 
private: 
    Color color; 
}; 

qui va déclarer et définir Foo::staticMetaObject.

+0

Wow, I n'a pas entendu de 'Q_GADGET' avant. Il semble donc que ce soit une version légère et statique de 'Q_OBJECT' qui peut être très utile dans certains cas (comme ceci). C'est triste, ce n'est pas documenté, au moins je ne pouvais pas le trouver. Cela le rend non officiel et donc pas garanti de travailler dans le futur :( – leemes

+0

@leemes Il est en fait mentionné brièvement à côté de 'Q_OBJECT' sur la page' QObject' (j'ai ajouté le lien ci-dessus) .Mais vous pouvez dire qu'il est assez bien caché, et je ne l'ai trouvé qu'en cherchant dans le code source du moc quelque chose d'autre. – alexisdm

1

Je ne connais pas grand-chose à propos de qt, mais si le constructeur de copie n'est pas autorisé alors il devrait y avoir une raison à cela (ce qui est discuté dans le lien que vous avez posté). Vous pouvez changer votre conception pour ne pas l'avoir.

Encore si vous insistez alors memcpy peut être votre dernier recours. Je ne le recommande pas personnellement, parce que vous devez faire attention à la copie en profondeur, vtable etc qui ne sont pas toujours triviales.

2

Vous pouvez certainement implémenter à la fois un constructeur de copie et un opérateur d'affectation de copie dans votre classe dérivée, mais c'est probablement un signe de mauvaise conception. Prenez cet exemple:

#include <iostream> 

class Base { 
public: 
    Base() {} 

private: 
    Base(const Base& other) { 
     std::cout << "Base copy constructor invoked!" << std::endl; 
    } 
}; 

class Derived : public Base { 
public: 
    Derived() {} 

    Derived(const Derived& other) { 
     std::cout << "Derived copy constructor invoked!" << std::endl; 
    } 
}; 

int main(int argc, char** argv) { 
    Derived a; 
    Derived b = a; 

    return 0; 
} 

Cela se compilera très bien. Cependant, comme prévu, lorsque vous exécutez le programme résultant, tout ce qui est imprimé est Derived copy constructor invoked!. Lorsque la classe de base déclare son constructeur de copie/opérateur d'affectation de copie comme privé, cela n'empêche pas les classes dérivées d'implémenter leurs propres versions. Il empêche simplement les classes dérivées d'appeler les versions de la classe de base.

Et c'est là que réside le problème: c'est toujours une bonne pratique de vous assurer de copier toutes les parties d'un objet, de sorte que vous avez en effet deux copies distinctes. Une partie de votre objet inclut les données appartenant à la classe de base. Vous devez donc toujours vous assurer d'appeler l'opérateur de copie/constructeur de copie de la classe de base pour vous assurer qu'une copie complète est effectuée. Mais ces données sont par nature non-copiables. Ainsi, il est impossible de copier toutes les parties de l'objet.

C'est à vous de choisir si vous voulez rester avec cette conception. Une chose importante à se demander est, est-ce que votre classe dérivée doit vraiment être copiable du tout? Si non, alors il n'y a pas de quoi s'inquiéter!