38 votes

Pourquoi une affectation à une classe de base est-elle valide, mais une affectation à une classe dérivée constitue une erreur de compilation ?

Il s'agissait d'une question d'entretien. Considérez les points suivants :

struct A {}; 
struct B : A {}; 
A a; 
B b; 
a = b;
b = a; 

Pourquoi est-ce que b = a; lancent une erreur, tandis que a = b; est parfaitement bien ?

62voto

Johannes Schaub - litb Points 256113

Étant donné que l'opérateur d'affectation par copie implicitement déclaré de l'option B cache l'opérateur d'affectation de copie implicitement déclaré de A .

Donc pour la ligne b = a seulement le operator= de B est un candidat. Mais son paramètre est de type B const& qui ne peut pas être initialisé par un A argument (vous auriez besoin d'une baisse). Vous obtenez donc une erreur.

24voto

Cicada Points 19550

Parce que chaque B est un A, mais pas chaque A est un B.

J'ai modifié les commentaires suivants pour rendre les choses un peu plus claires (j'ai modifié votre exemple) :

struct A {int someInt;}; 
struct B : A {int anotherInt}; 
A a; 
B b; 

/* Compiler thinks: B inherits from A, so I'm going to create
   a new A from b, stripping B-specific fields. Then, I assign it to a.
   Let's do this!
 */
a = b;

/* Compiler thinks: I'm missing some information here! If I create a new B
   from a, what do I put in b.anotherInt?
   Let's not do this!
 */
b = a;

Dans votre exemple, il n'y a pas d'attributs someInt ni anotherInt donc il podría travail. Mais le compilateur ne le permettra pas de toute façon.

6voto

Ken Bloom Points 27197

Il est vrai qu'un B est un A mais un A n'est pas un B mais ce fait n'est directement applicable que lorsque vous travaillez avec des pointeurs ou des références à des fichiers de type A et B 's. Le problème ici est votre opérateur d'affectation.

struct A {}; 
struct B : A {};

Est équivalent à

struct A {
   A& operator=(const A&);
}; 
struct B : A {
   B& operator=(const B&);
};

Donc quand tu assignes en dessous :

A a; 
B b; 
a = b;

L'opérateur d'affectation sur a peut être appelé avec un argument de b car un B est un A donc b peut être passé à l'opérateur d'affectation comme un A& . Notez que a L'opérateur d'assignation de l'UE ne connaît que les données qui se trouvent dans un fichier de type A et pas les trucs dans un B Les membres de B qui ne font pas partie de A sont donc perdus - c'est ce qu'on appelle le "découpage en tranches".

Mais quand vous essayez d'assigner :

b = a; 

a est de type A qui n'est pas un B donc a ne peut pas correspondre à la B& pour b L'opérateur d'affectation de l'UE.

On pourrait penser que b=a devrait juste appeler l'héritage A& A::operator=(const A&) mais ce n'est pas le cas. L'opérateur d'affectation B& B::operator=(const B&) cache l'opérateur qui serait hérité de A . Il peut être restauré à nouveau avec un using A::operator=; déclaration.

4voto

Caleb Points 72897

J'ai changé les noms de vos structures pour que la raison soit évidente :

struct Animal {}; 
struct Bear : Animal {}; 
Animal a; 
Bear b; 
a = b; // line 1 
b = a; // line 2 

Il est clair que tout ours est également un animal, mais tout animal ne peut être considéré comme un ours.

Parce que chaque B "est" A, toute instance de B doit aussi être une instance de A : par définition, elle a les mêmes membres dans le même ordre que toute autre instance de A. Copier b dans a fait perdre les membres spécifiques à B, mais remplit complètement les membres de a, ce qui donne une structure qui satisfait aux exigences de A. Copier a dans b, d'un autre côté, peut laisser b incomplet parce que B pourrait avoir plus de membres que A. C'est difficile à voir ici parce que ni A ni B n'ont de membres du tout, mais c'est pourquoi le compilateur autorise une affectation et pas l'autre.

3voto

Michael Burr Points 181287

Rappelez-vous que s'il n'y a pas d'opérateurs de copie-affectation explicitement déclarés, il y en aura un implicitement déclaré et défini pour toute classe (et les structs sont des classes en C++).

Pour struct A il aura la signature suivante :

A& A::operator=(const A&)

Et il effectue simplement une affectation par membre de ses sous-objets.

a = b; est OK parce que B correspondra à la const A& pour A::operator=(const A&) . Puisque seuls les membres de A sont "assignés par membre" à la cible, tous les membres de B qui ne font pas partie de A se perdre - c'est ce qu'on appelle le "découpage en tranches".

Pour struct B l'opérateur d'affectation implcit aura la signature suivante :

B& B::operator=(const B&)

b = a; n'est pas acceptable car A ne correspondra pas à la const B& argument.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X