La plupart des réponses n'expliquent pas quel est le problème réel du découpage en tranches. Elles n'expliquent que les cas bénins de découpage en tranches, et non les cas dangereux. Supposons, comme les autres réponses, que vous avez affaire à deux classes A
y B
où B
dérive (publiquement) de A
.
Dans cette situation, le C++ vous permet de passer une instance de B
à A
(et aussi au constructeur de copie). Cela fonctionne parce qu'une instance de B
peut être converti en un const A&
ce qui est ce que les opérateurs d'affectation et les constructeurs de copie attendent de leurs arguments.
Le cas bénin
B b;
A a = b;
Rien de grave ne se produit ici - vous avez demandé une instance de A
qui est une copie de B
et c'est exactement ce que vous obtenez. Bien sûr, a
ne contiendra pas certains des b
mais comment le faire ? C'est un A
après tout, pas un B
donc ça n'a même pas entendu à propos de ces membres, et encore moins serait capable de les stocker.
L'affaire des traîtres
B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!
Vous pourriez penser que b2
sera une copie de b1
après. Mais, hélas, c'est no ! Si vous l'inspectez, vous découvrirez que b2
est une créature de Frankenstein, faite de quelques morceaux de b1
(les morceaux qui B
hérite de A
), et quelques morceaux de b2
(les morceaux que seuls B
contient). Aïe !
Que s'est-il passé ? Eh bien, par défaut, le C++ ne traite pas les opérateurs d'assignation comme étant virtual
. Ainsi, la ligne a_ref = b1
appellera l'opérateur d'affectation de A
et non celle de B
. En effet, pour les fonctions non virtuelles, la fonction déclaré (formellement : statique ) (qui est A&
) détermine quelle fonction est appelée, par opposition à l'option réel (formellement : dynamique ) (qui serait B
puisque a_ref
fait référence à une instance de B
). Maintenant, A
L'opérateur d'affectation de l'utilisateur ne connaît évidemment que les membres déclarés dans l'article A
donc il ne copiera que ceux-là, laissant les membres ajoutés dans B
inchangé.
Une solution
Le fait de n'assigner que des parties d'un objet n'a généralement pas beaucoup de sens, mais le C++ ne fournit malheureusement pas de moyen intégré pour l'interdire. Vous pouvez cependant en créer un. La première étape consiste à rendre l'opérateur d'affectation virtual . Cela garantira que c'est toujours le réel qui est appelé, et non l'opérateur d'affectation du type déclaré de type. La deuxième étape consiste à utiliser dynamic_cast
pour vérifier que l'objet attribué a un type compatible. La troisième étape consiste à effectuer l'assignation réelle dans un membre (protégé !) assign()
puisque B
's assign()
vous voudrez probablement utiliser A
's assign()
pour copier A
des membres.
class A {
public:
virtual A& operator= (const A& a) {
assign(a);
return *this;
}
protected:
void assign(const A& a) {
// copy members of A from a to this
}
};
class B : public A {
public:
virtual B& operator= (const A& a) {
if (const B* b = dynamic_cast<const B*>(&a))
assign(*b);
else
throw bad_assignment();
return *this;
}
protected:
void assign(const B& b) {
A::assign(b); // Let A's assign() copy members of A from b to this
// copy members of B from b to this
}
};
Notez que, par pure commodité, B
's operator=
remplace de manière covariante le type de retour, puisqu'il connaît qu'il retourne une instance de B
.