Souvent, c'est une bonne idée d'avoir une classe de base abstraite pour isoler l'interface de l'objet.
Le problème est que la construction de copie, à mon humble avis, est à peu près cassée par défaut en C ++, avec des constructeurs de copie générés par défaut.
Alors, quels sont les pièges lorsque vous avez une classe de base abstraite et des pointeurs bruts dans les classes dérivées?
class IAbstract
{
~IAbstract() = 0;
}
class Derived : public IAbstract
{
char *theProblem;
...
}
IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???
Et maintenant, désactivez-vous proprement la construction de copie pour toute la hiérarchie? Déclarez que la construction de la copie est privée dans IAbstract
?
Existe-t-il des règles de trois avec des classes de base abstraites?
c++
abstract-class
Codeur
la source
la source
Réponses:
La construction de copie sur une classe abstraite doit être rendue privée dans la plupart des cas, ainsi que l'opérateur d'affectation.
Les classes abstraites sont, par définition, faites pour être un type polymorphe. Vous ne savez donc pas combien de mémoire votre instance utilise et vous ne pouvez donc pas le copier ou l'affecter en toute sécurité. En pratique, vous risquez de trancher: /programming/274626/what-is-the-slicing-problem-in-c
Le type polymorphe, en C ++, ne doit pas être manipulé par valeur. Vous les manipulez par référence ou par pointeur (ou tout autre pointeur intelligent).
C'est la raison pour laquelle Java a rendu l'objet manipulable uniquement par référence, et pourquoi C # et D ont la séparation entre les classes et les structures (le premier étant polymorphe et de type référence, le second étant non polymorphe et de type valeur).
la source
Vous pouvez, bien sûr, simplement le rendre protégé et vide, afin que les classes dérivées puissent choisir. Cependant, plus généralement, votre code est de toute façon interdit car il est impossible à instancier
IAbstract
- car il a une fonction virtuelle pure. En tant que tel, cela n'est généralement pas un problème - vos classes d'interface ne peuvent pas être instanciées et ne peuvent donc jamais être copiées, et vos classes plus dérivées peuvent interdire ou continuer à copier comme elles le souhaitent.la source
operator=(const Derived2&)
dansDerived1
.En rendant ctor et affectation privés (ou en les déclarant comme = supprimer en C ++ 11), vous désactivez la copie.
Le point ici est OERE vous devez faire cela. Pour rester avec votre code, IAbstract n'est pas un problème. (notez qu'en faisant ce que vous avez fait, vous affectez le
*a1
IAbstract
sous - objet à a2, en perdant toute référence àDerived
. L'affectation de valeur n'est pas polymorphe)Le problème vient avec
Derived::theproblem
. Copier un dérivé dans un autre peut en fait partager les*theproblem
données qui ne sont pas conçues pour être partagées (il existe deux instances qui peuvent appelerdelete theproblem
leur destructeur).Si tel est le cas, c'est
Derived
qu'il doit être non copiable et non assignable. Bien sûr, si vous rendez la copie privéeIAbstract
, puisque la copie par défaut en aDerived
besoin, elleDerived
ne sera pas non plus copiable. Mais si vous définissez le vôtreDerived::Derived(const Derived&)
sans appelerIAbtract
copy, vous pouvez toujours les copier.Le problème est dans Derived, et la solution doit rester dans Derived: s'il doit s'agir d'un objet dynamique uniquement accessible uniquement par des pointeurs ou des références, c'est Derived lui-même qui doit avoir
Il appartient essentiellement au concepteur de la classe Derived (qui devrait savoir comment fonctionne Derived et comment
theproblem
est géré) de décider quoi faire de l'affectation et de la copie.la source