7
votes

C ++ Comment se fait-il que je ne peux pas attribuer une classe de base à une classe enfant?

J'ai un code comme celui-ci:

void operator = (const Base& base_)
{
  Base::operator=(base_);
}


4 commentaires

Vous semblez manquer les bases du polymorphisme.


Un chat est un animal mais un animal n'est pas nécessairement un chat.


Mon intention était simplement de copier les membres communs entre la base et les classes d'enfants.


@ Shachris23: Et fais quoi avec les autres?


8 Réponses :


0
votes

Le problème est que vous ne pouvez pas attribuer une valeur constante à une variable non constante. Si vous le pouviez, vous pouvez potentiellement modifier la valeur constante en modifiant la variable non constante.

est-il nécessaire que la base soit constituée? Je demande parce que vous courez vraiment dans deux problèmes: Conscons-physique et héritage / polymorphisme.

Edit: Je suis le second devine moi-même maintenant concernant ma première déclaration. Toute clarification?


3 commentaires

Je pensais qu'au début, mais l'affectation est à l'enfant qui n'est pas Const, pas de base (qui est Const).


Droite ... N'est-ce pas le problème? Que se passera-t-il lorsqu'une méthode non constituée est appelée à l'enfant alors?


Enfant n'est pas Const, alors appelez une fonction de non-Const sur elle est OK. base est const, mais opérateur = prend un argument de base const et donc ça va aussi.



2
votes

Parce qu'une base n'est pas un enfant. Considérons:

class Child : public Base 
{ 
public: 
   SomeFunc();
};

void func() 
{ 
  const Base base; 
  Child child; 
  child = base; 
  child.SomeFunc();     // whatcha gonna call?
} 


1 commentaires

C'est un bon point, mais je pense qu'il demande pourquoi il obtient l'erreur du compilateur.



1
votes

Lorsque vous ne déclarez pas explicitement l'affectation de copie, le compilateur C ++ créera une affectation de copie pour vous. Dans votre cas, ce serait xxx

et c'est pourquoi vous obtenez cette erreur de compilateur. En outre, il est plus logique de renvoyer une référence à elle-même lorsque vous fournissez une affectation de copie car elle vous permettra de la chaîne opérateur ensemble.

Ce n'est pas une bonne idée de déclarer une affectation de copie comme celle-ci. xxx

Je ne vois pas pourquoi vous voulez seulement copier la partie de la classe de base lorsque vous effectuez une affectation de copie.


3 commentaires

Eh bien, l'erreur de compilation le dit clairement. Je n'ai pas ton commentaire. Il demande pourquoi il obtient l'erreur de compilation.


Surcharger l'opérateur d'affectation, tout en étant la raison pour laquelle les rapports du compilateur sont totalement orthogonaux au vrai problème ici, qui est que l'affiche n'a pas compris l'idée de polymorphisme. Votre réponse est comme indiquer aux gens d'utiliser const_cast pour contourner la const-correction. Cela pourrait résoudre l'erreur, mais ce n'est pas le vrai problème.


Je lis la question à nouveau, voici ce qu'il a demandé: ma question est la suivante: puisque l'enfant dérive de la base (par conséquent, il devrait hériter de l'opérateur de la base =), comment se fait-il quand la déclaration enfant = base; est exécuté, je reçois une erreur de compilateur comme ceci: bla, bla ... peut-être que j'ai terminé sa question.



7
votes

Vous ne pouvez pas attribuer la base à l'enfant, car l'enfant pourrait ne pas être basé. Pour utiliser l'exemple d'animal typique, ce que vous avez ici est:

Dog* const dog;
Animal* animal;
animal = dog;


3 commentaires

@Poita_: Merci pour une bonne réponse. Cependant, j'ai une question. Quand nous faisons chiens = animal, comment ça ne peut pas simplement copier la partie animale au chien? Est-ce que tu vois ce que je veux dire? par exemple. Dites que la classe d'animaux a un membre privé appelé Int numlegs; Ensuite, quand nous faisons chien = animal, c ++ ne devrait que copier animal :: numlegs et l'attribuer à chien :: nombres.


C'est-à-dire qu'il copie les parties communes entre les 2 classes.


Je vois ce que tu veux dire, mais que se passerait-il aux parties du chien qui ne sont pas spécifiées par l'animal? Vous pourriez éventuellement concevoir une langue pour fonctionner de cette façon, mais le problème fondamental ici est que cela n'a aucun sens.



13
votes

La norme constitue la raison de votre question spécifique dans 12.8 / 10 "Objets de classe de copie" (accent ajouté):

Comme un opérateur d'affectation de copie est implicitement déclaré pour une classe si non déclarée par l'utilisateur, Un opérateur d'affectation de copie de la classe de base est toujours masqué par l'opérateur d'affectation de copie d'une classe dérivée (13.5.3 ).

Donc, depuis qu'il y a un opérateur implicitement déclaré = (const enfant &) lorsque le compilateur exécute la résolution de recherche / surcharge de noms pour un enfant :: opérateur = () , La signature de la fonction de base n'est jamais même considérée (elle est cachée).


7 commentaires

Ouais et comme je l'ai noté dans mon message, vous pouvez utiliser "Utiliser" pour tirer cet opérateur = retour dans la portée des enfants. (Que je trouve effrayant)


J'ai l'impression que caché est une mauvaise formulation de la norme. BASE VOID :: Opérateur = (const base & base_); et avidité du Chili :: opérateur = (const base & base_); sont intrinsèquement différents car on attribue une base à une base et l'autre à un enfant. Semble idiote pour dire que c'est caché quand ce sont des fonctions différentes. Il devrait être caché parce que ce n'est pas logique pour cette fonction d'exister dans la classe enfant.


Le nom se cache pour opérateur = () est manipulé comme pour tout autre nom de la fonction pouvant être masqué avec une différence majeure: si vous ne déclarez pas opérateur = () Pour une classe, vous obtenez une déclaration implicite afin que le nom se cache est là que vous le souhaitez ou non et si vous auriez aimé le nom de ne pas être caché, il n'y a pas grand chose que vous pouvez faire à ce sujet, sauf pour définir un opérateur ( ) dans la classe dérivée qui transmet la mise en œuvre de la classe de base. C'est pourquoi la norme appelle le nom de cache le comportement spécifiquement pour opérateur = () .


Mais cela est toujours absurde. L'opérateur implicite = () d'une classe prend un type Const & à ce type de classe, la valeur par défaut pour la base serait base :: opérateur = (const base &) et pour enfant serait enfant :: opérateur = (const enfant &) . Ils prennent différents paramètres, il ne s'agit pas de la fonction de fonction régulière, car la fonction de fonction régulière ne cache pas une fonction de classe de base qui accepte un paramètre différent. Je pense toujours que son formulaire médiocre qui ne fait que confondre la manière dont la fonction de fonction fonctionne.


@Michael Anderson - J'ai oublié d'utiliser en utilisant pour tirer le nom de la classe de base.


@ 0xc0deface: la règle pour le nom de la fonction Cache en C ++ est si le nom est déclaré dans la classe dérivée, toutes les surcharges dans les classes de base pour ce nom sont cachées sauf si elles sont explicitement tirées. Il y a une bonne discussion ici: Stackoverflow.com/questions/1628768/... ainsi que dans l'élément" Exceptionnel C ++ "d'Herb Sutter's 34 -" Nom Caching et l'interface Principal - Partie 4 "


Grand lien! Je n'ai pas réalisé que ce nom se cache caché toutes les fonctions de ce nom indépendamment des surcharges!



1
votes

La réponse est plus effrayante que je pensais. Vous peut faire cela. La norme Evens a un peu de note à ce sujet à la section 7.3.3 Le problème principal est que l'opérateur = qui est défini par défaut dans l'enfant masque l'opérateur = de la base afin de pouvoir l'importer dans la portée de la classe à l'aide de "Utilisation".

Ceci compile bien pour moi. Cependant, je trouve cette idée très effrayante et potentiellement affreuse avec un comportement indéfini si des trucs de l'enfant doivent être initialisés. xxx

modifier: rendu la base const à nouveau et évitez "UNITIALISÉ Const "Erreur.


0 commentaires

10
votes

Le code ci-dessous est le comportement que je voulais depuis le début et compile, xxx

Je ne savais jamais que vous pouvez expliquer explicitement quelque chose comme: xxx < / Pré>

Quoi qu'il en soit, j'ai beaucoup appris. Merci pour toutes les personnes qui ont posté des réponses ici.



2
votes

Ce n'est pas une réponse, mais la question de savoir comment a répondu. C'est le pourquoi-pas.

Les cours devraient signifier quelque chose. Il devrait y avoir des choses utiles que vous pouvez dire sur tout objet bien formé dans la classe («invariants de classe»), car de cette façon, vous pouvez raisonner sur les programmes sur la base de la façon dont une classe fonctionne. Si une classe est juste une collection de membres de données, vous ne pouvez pas faire cela et faire raisonner le programme sur la base de chaque élément de données individuel.

L'opérateur d'affectation proposé modifiera chaque membre de données de la classe de base, mais ne touchera pas ceux définis dans la classe enfant. Cela signifie une des deux choses.

Si un objet enfant est bien formé après un autre objet de base est déversé dans son composant de base, cela signifie que les éléments de données supplémentaires n'ont aucune connexion réelle avec les éléments de données de base. Dans ce cas, l'enfant n'est pas vraiment un sous-type de base et l'héritage public est la mauvaise relation. L'enfant n'est pas dans une relation "is-a" à la base, mais plutôt une relation "a-une". Ceci est généralement exprimé par la composition (enfant a un membre de données de base) et peut également être exprimé par héritage privé.

Si un objet enfant n'est pas bien formé après un autre objet de base dans son composant de base, l'opérateur d'affectation proposé est une méthode très rapide et commode pour gâcher une variable.

Pour un exemple, prenons une classe de base de mammifère et deux classes d'enfants, cétacé et chauve-souris. L'animal a des choses comme la taille et le poids, et les classes d'enfants ont des champs liés à ce qu'ils mangent et comment ils bougent. Nous attribuons un cétacé à un mammifère, raisonnable (tous les champs spécifiques à un cétacé sont juste abandonnés) et nous assignons maintenant que le mammifère à une batte. Soudainement, nous avons une batte de vingt-tonnes.

Avec un ensemble de classes bien conçu, la seule façon de concevoir cet opérateur d'affectation, même si les classes d'enfants n'ont aucun membre de données supplémentaire, seul un comportement supplémentaire, et même alors il est suspect.

Par conséquent, mon seul conseil est de refaire votre conception de classe. Beaucoup de gens, en particulier ceux familiarisés avec Java et d'autres langues qui ont tendance à des hiérarchies d'héritage profondes, utilisent beaucoup trop d'héritage en C ++. C ++ fonctionne généralement mieux avec un grand nombre de classes de base et une hiérarchie relativement peu profonde.


0 commentaires