2009-07-06 4 views
1

Il y a une arborescence de classes enracinée à CBase (single-inheritance). CSub est dérivé de CBase, et d'autres classes dérivées de CBase ou CSub, et CBase ayant des fonctions d'élément virtuel.Comment puis-je m'assurer de l'assignabilité dans cet arbre?

Toutes les classes doivent être assignables. Certaines classes ont des membres (pointeurs) que nécessitent un traitement spécial dans l'affectation (pointeurs). Comment puis-je m'assurer de l'assignabilité dans cet arbre de classes? D'abord, j'ai pensé que je devais rendre "opérateur =" virtuel. Puis j'ai réalisé que c'était faux. Ensuite est du tout possible:

  CBase *x = new CSub; 
     CBase *y = new CSub; 
     *x = *y; // is this okay ? 

Sinon, comment dois-je attribuer * y * à x sans mal baissés? J'ai beaucoup de questions ici.

Si j'ai besoin de lancer à chaque fois que j'attribue via CBase *, cela ne correspond pas à , n'est-ce pas? Ai-je besoin d'insérer des vérifications de type dans "operator =" pour vérifier que lhs et rhs ont le même type? etc etc. Les exemples sont les bienvenus.

Merci Viki

+1

Quel long titre! Pourriez-vous svp résumer d'une manière ou d'une autre le titre pour l'adapter dans une ligne simple? –

+0

J'ai pris une photo. Viki, quel est votre résumé? (J'étais tellement tenté de modifier à "Ceci est l'arbre".) – Nosredna

Répondre

0

Pourquoi ont-ils besoin d'être cessible? Vous ne serez pas en mesure de le faire en toute sécurité/correctement et encore permettre de nouvelles sous-classes, ou vous finirez par écrire le code d'une manière où tous les types d'objets sont entièrement connus au moment de la compilation et vous pouvez éviter le problème entièrement. Je suggère alternativement, une méthode virtual CBase* copy() const. Il peut utiliser des retours covariants dans les sous-classes. Aucun downcasting requis, et aucun potentiel de tranchage.

0

Le code que vous présentez effectue en effet un "mauvais downcasting" (triturer les choses comme un fou) - il fonctionnera toujours "accidentellement" si CBase est une classe concrète avec un opérateur d'affectation normal et utilisable (et encore Je répète le conseil de Haahr: ne pas sous-classer [si vous avez une vtable ou des données supplémentaires, c'est-à-dire un sous-classement "sérieux" ;-)] de classes concrètes, seulement abstraites - parmi d'autres avantages vous serez sauvé un tel "code de compilation propre" accidentel et trompeur qui risque de faire de mauvaises choses quand il s'exécute). Si vous voulez que chaque sous-classe de l'arbre puisse être assignée à partir d'une instance de n'importe quelle autre (et si CBase a un opérateur d'affectation alors par le principe de Liskov), vous devez assigner une sous-classe assignable à CBase et assurez-vous qu'il traite toute sous-classe ignorée par cette route (en ignorant toutes les données supplémentaires que la classe sœur inconnue a pu ajouter, soupir) - ou essayez une route plus virtualisable avec par exemple une méthode virtual void assign(const CBase*) (qui ne nécessite pas de CBase pour être concrète, btw ;-).

Ceci ne résout toujours pas le problème d'avoir possiblement jusqu'à N cases au carré; pour cela, vous pouvez essayer Visiteur ou (probablement mieux) Acyclic Visitor de Uncle Bob (encore mieux d'utiliser cela avec des méthodes nommées qu'avec operator=, cependant ;-).

1

C'est une question simple je crois.Vous aurez besoin de mettre en œuvre « opérateur d'affectation virtuelle » qui nécessitera upcast IMHO:

class CBase { 

... 
    virtual assign(const CBase & from) = 0; 
... 
}; 

class CSub { 
... 
    void operator=(const CSub & from) { assign(from); } 

    virtual assign(const CBase & from) { 
     if (dynamic_cast<const CSub &>(from)) { 
     ... implement simple copy here 
     } else { 
     ... you'll have to decide what to do with other types 
     } 
    } 
}; 
0

Ici, vous traitez avec des pointeurs de sorte que vous devez être prudent:

CBase *x = new CSub; 
CBase *y = new CSub; 
*x = *y; // is this okay ? 

Le code ci-dessus provoque une mémoire fuite puisque x n'a pas libéré l'objet CSub vers lequel il pointe. Lorsque vous traitez des pointeurs, vous devez prendre soin du contenu précédent qu'ils peuvent pointer vers.

Vous pouvez utiliser auto_ptr ou un autre pointeur intelligent à cet effet:

std::auto_ptr<CBase> x(new CSub); 
std::auto_ptr<CBase> y(new CSub); 
x = y; // x now owns the obj that y pointed to, x's old obj has been deleted 

avec la bibliothèque de modèles de boost:

boost::shared_ptr<CBase> x(new CSub); 
boost::shared_ptr<CBase> y(new CSub); 
x = y; // now x and y point to same obj, old obj x pointed to has been deleted. 

Ok ce n'est pas ce que vous demandez, mais pensé que je pourrais couvrir cela juste au cas où.

à la partie d'affectation de votre question

Pour être du bon côté, vous devez créer opérateur =() pour chaque classe. Ceci est le moyen de contrôler l'affectation puisque vous pouvez avoir des membres qui sont des pointeurs etc.

class CBase // should be made abstract 
{ 
public: 
    CBase() { ; } 
    CBase& operator=(CBase& in) { copy members } 
}; 

class CSub : public CBase 
{ 
public: 
    CSub() {;} 
    CSub& operator=(CSub& in) { copy members from in, and base class members } 
}; 

HTH