2010-01-13 3 views
2

Je voudrais écrire un groupe de classe D_I (alias I = 1,2, ... juste que je veux dire '_I' être le nom propre à la classe, pas entier) qui gère certaines opérations (booléennes, par exemple) F avec la même signification pour toutes les classes. Mais aussi je veux opérer avec des "sommes" d'objet de telles classes, différentes classes du groupe pourraient être "ajoutées" ensemble. L'opération mentionnée de la somme de cet objet dépend du fonctionnement correspondant des objets ajoutés.C++ Problème avec le code opérateur inheritage: ai-je besoin de copier le même code pour toutes les classes dérivées?

Voilà pourquoi je vais créer la classe de base B commun pour gérer le fonctionnement de « sommation »:

class B 
    {public: 
     B(): LEFT(NULL), RIGHT(NULL) {}; 
     B (const SpBrd& S); 
     B (const B& A, const B B); 
     ~B(); 
      {if (LEFT != NULL) delete LEFT; // NULL if it is default B or D_I 
      if (RIGHT != NULL) delete RIGHT; // NULL if it is default B or D_I 
      } 
     B operator + (const B& S) const; 
     bool F(const Point& P) const; 
      {bool b = aF(P); 
      if (LEFT != NULL) b = b && LEFT->F(P); // this is how F depends 
      if (RIGHT != NULL) b = b && RIGHT->F(P); // of added classes 
      } 
    protected: 
     virtual bool aF(const Point& P) const {return true;}; // Default! 
     B* LEFT; // Pointer to left operand of "sum" 
     B* RIGHT; // Pointer to right operand of "sum" 
     // (since it might point to D_i class too as well) 
    }; 

donc cette façon dérivée classe D est facile à écrire, car il ne devrait gérer son constructeur et aF :

class D_I: public B 
    {public: 
     D() {...}; 
    protected: 
     virtual bool aF (const Point& P) const 
      {// return something here 
      } 
    }; 

Le problème est le suivant: COMMENT ÉCRIRE opérateur + et constructeur de copie de la classe B:

B:B (const B& S) 
     {if (S.LEFT != NULL) LEFT = new B (S.LEFT) 
     if (S.RIGHT != NULL) RIGHT = new B (S.RIGHT)    
     }; 
    B B:operator + (const B& S) const 
     {SpBrd S; 
     S.LEFT = new B (*this); 
     S.RIGHT = new B (S); 
     }; 

ne produirait pas de résultat correct parce que 'new B ...' devrait être remplacé par 'new D_I ...' si 'this', 'S' dans 'operator +' ou 'S.LEFT' ou 'S .RIGHT 'dans le constructeur de copie ne fait pas référence à un objet de classe B exactement mais à un objet de classe D_I. Donc, je ne peux pas trouver un moyen de rendre B :: operator + savoir de quelles sources de somme il s'agit car il pourrait y avoir une grande quantité de D_I et ils seront ajoutés de temps en temps. Que dois-je faire pour écrire tout cela correctement?

Je ne peux pas écrire des parties de l'opérateur D_I + (D_J) et D_I :: D_I (const D_J) pour toutes les paires de (D_I, D_J), ce n'est pas juste! (Cela représenterait quelque chose comme des frontières (surface) dans l'espace.La fonction F représente être à l'intérieur de la frontière - c'est-à-dire pour vérifier si le point est à l'intérieur d'un corps spatial. (opérateur +) des corps spatiaux à l'intérieur des deux autres bordures Exemples: intersection de deux bordures de sphères - zones intérieure et extérieure - produit une coquille sphérique appelée anneau 3D, peut aussi revoir la bordure sphérique d'intersection avec rayon R et angle solide de 10 * 10 degrés autour de pôles et ainsi de suite)

+0

Excuse StackOverflow pour ne pas respecter mes sauts de paragraphe, mais nonless: Qu'est-ce que 'SpBrd'? L'opérateur + en bas n'a pas de sens syntaxique. 'classe B' ne compilera même pas dans ma tête parce que ses noms de cteurs et de dtor sont faux. Aussi, pensez à reformuler votre question. Ce n'est pas clair: "Classe de groupe"? Évitez la voix passive. Peu importe comment j'essaie de le lire, "1) dans l'opérateur + si * this et S ne sont pas B mais un de D_I devrait être" new D_I "ne me lit même pas comme une phrase anglaise correcte. Idem pour le point 2). Je vais m'arrêter ici. – wilhelmtell

+0

Oh, mon Dieu, je suis vraiment désolé ... En posant cette question, j'ai juste pris un fragment de mon programme en supprimant des fonctions supplémentaires, et en changeant les noms de classes comme SpBrd et SphBrd en Base et Dériver (B et D en abrégé) pour ne pas confondre les lecteurs, mais il est tout à fait le contraire ... J'ai aussi paraphrasé ce paragraphe. Encore pardon. – Nick

+0

J'ai mis à jour ma réponse. Je pense que cela répond à la question que vous posez maintenant, même si je suis encore un peu perplexe à ce sujet. – Omnifarious

Répondre

3

Votre question est source de confusion, et je me sens précipité ici, mais je vais le dire.

Généralement, les opérateurs sont déclarés en dehors d'une classe en tant que fonctions non membres et utilisent les fonctions membres pour atteindre leur objectif. C'est ainsi que les deux arguments à l'opérateur peuvent participer également à la résolution de surcharge.

Dans ce cas, il semble que vous essayez de créer une sorte d'arborescence d'analyse basée sur les opérateurs C++. Dans ce cas, le code comme cela pourrait être ce que vous cherchez:

const B operator +(const B &a, const B &b) 
{ 
    return B(a, b); 
} 

Notez que cela fonctionnera même si quelque chose provenant de B est l'une des opérandes. Et si j'ai raison sur ce que vous faites, vous aurez probablement un type de noeud spécial pour deux choses ajoutées l'une à l'autre, et l'opérateur + peut le retourner.


Maintenant que je vois votre version clarifiée, voici une réponse différente. D'abord, je remets en question votre utilisation de l'opérateur + de cette manière. La surcharge de l'opérateur entraîne souvent des surprises pour les personnes utilisant votre classe, et à moins que votre utilisation de l'opérateur + ne se comporte comme les gens s'attendent à ce que l'opérateur + se comporte en général, cela causera plus de problèmes qu'il résout. Mais votre véritable question semble tourner autour de la création de copies d'objets de type inconnu sans beaucoup de code répétitif. Quand je vois du code répétitif, j'ai tendance à penser à des modèles. Voici un exemple de code qui est probablement un peu plus complexe que ce dont vous avez besoin qui utilise des modèles et je pense que cela résout votre problème.

#include <memory> 
#include <iostream> 

template <class BaseType, class DerivedType> 
class Cloneable { 
public: 
    virtual ~Cloneable() {} 

    ::std::auto_ptr<DerivedType> clone() const { 
     return ::std::auto_ptr<DerivedType>(static_cast<DerivedType *>(i_clone())); 
    } 

protected: 
    virtual BaseType *i_clone() const { 
     return new DerivedType(dynamic_cast<const DerivedType &>(*this)); 
    } 
}; 

class A : public Cloneable<A, A> { 
public: 
    A() {} 
    A(const A &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the A at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~A() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the A at " << voidme << "\n"; 
    } 
}; 

template <class Derived> 
class B : public A, public Cloneable<A, Derived> { 
public: 
    B() {} 
    B(const B &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the B at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~B() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the B at " << voidme << "\n"; 
    } 
    // Make sure clone can be mentioned in derived classes with no ambiguity 
    using Cloneable<A, Derived>::clone; 

protected: 
    // Force dominance rules to choose the correct i_clone virtual function. 
    virtual A *i_clone() const { 
     return Cloneable<A, Derived>::i_clone(); 
    } 
}; 

class C : public B<C> { 
public: 
    C() {} 
    C(const C &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the C at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~C() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the C at " << voidme << "\n"; 
    } 
}; 

class D : public B<D> { 
public: 
    D() {} 
    D(const D &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the D at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~D() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the D at " << voidme << "\n"; 
    } 
}; 

int main(int argc, const char *argv[]) 
{ 
    C c; 
    D d; 
    ::std::auto_ptr<A> cptr(c.clone()); 
    ::std::auto_ptr<A> dptr(d.clone()); 
    cptr = dptr->clone(); 
    return 0; 
} 

J'ai fait les méthodes de i_clone et clone de telle sorte que chaque classe se retrouverait avec une version de clone qui a renvoyé un pointeur vers propre type de la classe. Je dois également avoir la déclaration using dans la classe modèle B pour m'assurer qu'il n'y a pas d'ambiguïté dans laquelle le clone sera appelé dans les classes dérivées.

Notez que les classes C et D ne contiennent pas de code répétitif relatif à la création de clones d'eux-mêmes.

Alors que ne le savais pas à l'époque, cela semble être encore une autre réinvention de langage The Curiously Recurring Template appliqué à polymorphic copy construction.

+0

Quel est le point de retourner 'const B'? Je l'ai vu un certain nombre de fois mais je ne vois pas le point puisque je peux parfaitement écrire 'B result = a + b;'. Est-ce que j'ai râté quelque chose ? –

+0

Je retourne toujours const X à partir des fonctions pour éviter de construire des constructions bizarres comme '(a + b) = 5;'. – Omnifarious

+0

@Matthieu: il rend les expressions comme 'a + b = c' invalides, comme elles le sont pour les types intégrés. C'est plus un problème lorsque les opérateurs de déréférencement renvoient des copies - vous voulez que 'a [i] = b' échoue à compiler, pas à assigner en silence à un temporaire. –

2

Il est un peu difficile à suivre exactement ce que vous essayez de faire (ce qui est SpBrd) mais vous pourriez envisager de faire une nouvelle fonction virtuelle comme ceci:.?

class B 
{ 
    ... 
    virtual B *Clone() const { return new B(*this); } 
    ... 
}; 
class D: public B 
{ 
    ... 
    virtual D *Clone() const { return new D(*this); } 
    ... 
}; 

qui peut alors être utilisé comme ceci:

S.LEFT = this->Clone(); 
S.RIGHT = S.Clone(); 
+0

Merci beaucoup, cela devrait bien fonctionner, c'est certain. Le seul inconvénient est que je devrais créer une telle fonction de copie pour toutes les classes D_I avec presque le même code! Ce serait génial, si C++ permettait d'utiliser pas 'new Type (arg)' mais 'pointer_to_object_of_Type -> operator new (arg)', mais je ne trouve pas si c'est possible. Merci encore et désolé pour SpBrd, bien sûr, il était B. Et D_I serait SphBrd, AlphaBrd, DeltaBrd et ainsi de suite ... – Nick

+1

Ces fonctions virtuelles devraient probablement être const-qualifié, à savoir B * Copy() const –

+1

Ce modèle est communément utilisé en utilisant le verbe 'cloner' plutôt que 'copier' - en nommant votre fonction Clone() traduira probablement l'intention plus clairement. Voir par exemple l'interface Java Cloneable: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Cloneable.html –

Questions connexes