Découpage C ++ provoquant une fuite / un comportement non défini / un crash

Existe-t-il un exemple d’effet de découpage en tranches d’objects C ++ pouvant provoquer un comportement non défini, une fuite de mémoire ou un blocage dans un ensemble de code par ailleurs correct? Par exemple, lorsque les classes A et B (héritées de A ) sont correctes et saines, mais que l’appel d’un void f(A a) provoque manifestement des choses désagréables.

Il est nécessaire pour former une question de test. Le but est de savoir si le participant est conscient du phénomène de découpage en utilisant un extrait de code dont la correction ne doit en aucun cas être une question d’opinion.

Si A est effectivement “correct et correct”, le découpage en tranches (copie du sous-object de base) est bien défini et ne causera aucun des problèmes que vous avez mentionnés. Le seul problème que cela causera est un comportement inattendu, si vous vous attendez à ce que la copie se comporte comme B

Si A n’est pas correctement copiable, le découpage en tranches causera tous les problèmes liés à la copie d’objects de ce type. Par exemple, s’il possède un destructeur qui supprime un pointeur tenu par un object et que la copie crée un nouveau pointeur sur la même chose, vous obtiendrez un comportement non défini lorsque les deux destructeurs suppriment le même pointeur. Ce n’est pas un problème avec le découpage en tant que tel, mais avec une sémantique de copie non valide de l’object découpé.

Vous pouvez toujours construire un tel exemple

 struct A { A() : invariant(true) {} virtual void do_sth() { assert(invariant); } protected: bool invariant; }; struct B : A { B() { invariant=false; } virtual void do_sth() { } }; void f(A a) { a.do_sth(); } 

Bien entendu, cela pourrait être évité dans A lorsque le constructeur de copie / l’opérateur de vérification ne vérifie pas si l’invariant est vrai.

Si l’invariant est plus implicite que ma valeur booléenne, ces choses peuvent être très difficiles à voir.

le découpage d’object n’est vraiment un problème que si vous manipulez des classes dérivées à l’aide de pointeurs ou de références à leur classe de base. Ensuite, les données supplémentaires de la classe dérivée restnt inchangées, alors que celles de la partie base peuvent être modifiées. Cela peut casser les invariants de la classe dérivée. Pour un exemple simple, voir http://en.wikipedia.org/wiki/Object_slicing

Cependant, je considérerais cela comme un défaut de conception (de la classe dérivée). Si l’access à une classe par n’importe quelle méthode légale, y compris celles sockets par un pointeur ou un argument de référence à la classe de base, peut rompre ses invariants, la classe est mal conçue. Une façon d’éviter cela est de déclarer la base private , de sorte que la classe dérivée ne puisse pas être légalement accessible via sa base.

Un exemple serait:

 class A { int X; A(int x) : X(x) {} void doubleX() { X+=X; } /* ... */ }; class B : public A { int X_square; B(int x) : A(x), X_square(x*x) {} /* ... */ }; B b(3); B.doubleX(); /// BX = 6 but B.X_square=9 

D’après cet exemple, il est également évident qu’il s’agit d’un simple défaut de conception dans B. Dans cet exemple, fournir

 void B::doubleX() { A::doubleX(); X_squared=X*X; } 

ne résout pas le problème, comme

 A&a=b; a.doubleX(); 

brise toujours l’invariant. La seule solution ici est de déclarer la base A privée ou, mieux, de faire de A un membre privé, plutôt que de base, de B