2012-06-27 3 views
1

Tout, cela semble être un problème simple mais ça me donne des crises. Disons que j'ai une classe de base C++ appelée Animal et des classes dérivées Cat, Chien, Cheval, Hippopotame, Serpent. J'ai une autre classe appelée Zoo qui contient une liste (Qt List dans ce cas où la classe de base Animal est un QObject) d'objets Animal. Maintenant, ce que je veux faire est de créer un objet Zoo et être capable de le copier dans un autre objet Zoo et de faire en sorte que le Zoo de destination possède sa propre copie des Animaux. Le problème est dans le constructeur de copie de zoo parce qu'il pense qu'il a une liste d'animaux qu'il appelle le constructeur de copie pour l'animal au lieu de chien, chat, etc. car les compilateurs devraient faire. Pour contourner cela, j'ai changé la liste des animaux en une liste de pointeurs d'animaux, puis j'ai créé mon propre constructeur de copie d'animaux et un opérateur d'affectation. Chacun d'eux appelle la méthode Animal :: Clone (Animal & rhs) qui à son tour appelle chaque classe dérivée de la méthode Animals Clone() qui renvoie un nouveau pointeur alloué à partir du tas avec une copie de ses données. Tout fonctionne bien mais je continue à penser qu'il me manque une solution plus élégante. Donc ma question est, quand vous avez un conteneur avec des objets d'un type, comment pouvons-nous copier la classe qui contient le conteneur? J'espère que cela à du sens.Copie conteneur d'éléments avec héritage

+2

Avez-vous essayé de rendre le constructeur de copie pour 'Animal' virtuel? – Daniel

+0

Je suis d'accord avec Daniel, le seul moyen de surcharger une fonction de la classe parente pour que la fonction soit une fonction virtuelle, sinon le code est généré dans le compilateur. –

+0

Le problème survient lorsque Animal hérite de QObject. Si je comprends bien, Qt interdit les copies d'objets qui héritent de QObject. Est-ce que quelqu'un sait si c'est correct? – Brad

Répondre

2

Ce que vous avez fait est presque la façon idiomatique de le faire en C++. La méthode virtuelle clone() est en fait connue sous le nom de constructeur de copie virtuelle idiom. En général, vous le déclarez comme une méthode abstraite virtuelle de la signature suivante.

Animal * clone() const = 0; 

Notez qu'il ne prend pas un paramètre rhs, il retourne simplement un clone de lui-même. Étant donné un exemple d'un animal Animal * myPet, vous pouvez obtenir une copie d'un en disant

Animal * mySecondPet = myPet->clone(); 

Vous êtes obligé de le mettre en œuvre dans les classes dérivées si elles doivent être instancié.

[Ed:] Comme vous l'avez découvert, il est impossible pour une classe de conteneur pour contenir les éléments directement et non pas par des pointeurs, en raison de la inhérente slicing problem dans la conception de C++.

En C++, votre solution est un idiome: elle est aussi élégante que possible, et tous ceux qui la voient, qui connaissent leur C++, doivent immédiatement savoir ce que vous voulez dire. Idiomes et certains modèles de conception font partie de la langue. Vous ne pouvez pas vous appeler compétent dans une langue, qu'elle soit humaine ou programmée, sans connaître les idiomes.

Dans plusieurs de mes projets, j'utilise une classe d'interface (toutes les méthodes virtuelles abstraites), appelée Cloneable, qui consiste uniquement en cette méthode de clonage. Il est alors facile de forcer une classe de base à être clonable: il suffit d'hériter de Cloneable. Je souhaite que QEvent était Cloneable de cette façon, par exemple. En l'état, il est impossible de dupliquer QEvents pour les publier dans plusieurs files d'attente d'événements.

La suggestion de Daniel "Avez-vous essayé de faire le constructeur de copie pour Animal virtuel?" ne peut pas être pris à la lettre. Il n'existe pas de constructeur de copie virtuelle ou de constructeur en C++ pour une raison simple: avant la construction d'un objet, sa table de méthode virtuelle n'est pas finalisée. Plus précisément, le code du constructeur d'Animal s'exécutera avant que les constructeurs des classes dérivées ne soient appelés pour échanger le pointeur de la table de méthode virtuelle avec celui de la classe dérivée. Ainsi, si vous avez effectué des appels de méthode virtuelle dans le constructeur, ils ne vont pas à une classe dérivée de votre classe. C'est tout.

+0

Cela a du sens. Et puis dans la classe Zoo, implémentez le constructeur de copie et l'opérateur d'affectation pour parcourir la liste QList appelant la méthode clone() de chaque animal, n'est-ce pas? – Brad

+0

C'est correct. –