2009-12-11 5 views
0

Je travaille actuellement sur un projet dans lequel j'aimerais définir une interface de 'collection' générique qui peut être implémentée de différentes manières. L'interface de collection doit spécifier que la collection a des méthodes qui retournent les itérateurs par valeur. L'utilisation de classes que les pointeurs wrap je suis venu avec ce qui suit (très simplifié):Hiérarchie des classes C++ pour la collection fournissant des itérateurs

Collection.h

class Collection 
{ 
    CollectionBase *d_base; 
public: 
    Collection(CollectionBase *base); 

    Iterator begin() const; 
}; 

inline Iterator Collection::begin() const 
{ 
    return d_base->begin(); 
} 

CollectionBase.h

class CollectionBase 
{ 
public: 
    virtual Iterator begin() const = 0; 
    virtual Iterator end() const = 0; 
}; 

Iterator.h

class Iterator 
{ 
    IteratorBase *d_base; 
public: 
    bool operator!=(Iterator const &other) const; 
}; 

inline bool Iterator::operator!=(Iterator const &other) const 
{ 
    return d_base->operator!=(*other.d_base); 
} 

IteratorBase.h

class IteratorBase 
{ 
public: 
    virtual bool operator!=(IteratorBase const &other) const = 0; 
}; 

En utilisant cette conception, les différentes implémentations de la collection dérivent de CollectionBase et peuvent retourner leurs itérateurs personnalisés en retournant un Iterator qui enveloppe une mise en œuvre spécifique de IteratorBase.

Tout va bien et dandy jusqu'ici. J'essaie actuellement de comprendre comment implémenter operator!= cependant. Iterator transmet l'appel à IteratorBase, mais comment l'opérateur doit-il être implémenté? Une méthode simple serait de simplement transtyper la référence IteratorBase au type approprié dans les implémentations de IteratorBase, puis d'effectuer la comparaison spécifique pour l'implémentation de IteratorBase. Cela suppose que vous jouerez bien et ne passerez pas deux types différents d'itérateurs.

Une autre façon serait d'effectuer un certain type de vérification de type qui vérifie si les itérateurs sont du même type. Je crois que cette vérification devra être faite au moment de l'exécution, et vu qu'il s'agit d'un itérateur, je préférerais ne pas effectuer de vérification du type d'exécution coûteuse en operator!=.

Est-ce qu'il me manque des solutions plus belles ici? Peut-être y a-t-il de meilleures conceptions de classe alternatives (la conception actuelle est une adaptation de quelque chose que j'ai appris dans un cours de C++ que je prends)? Comment aborderiez-vous cela?

Editer: Pour tout le monde me pointant vers les conteneurs STL: Je suis au courant de leur existence. Cependant, je ne peux pas les utiliser dans tous les cas, car la quantité de données à traiter est souvent énorme. L'idée ici est de mettre en œuvre un conteneur simple qui utilise le disque comme stockage au lieu de la mémoire.

+2

oh gawd ... C++ déjà * a * le concept de conteneurs et itérateurs. Si vous jouiez avec cela, vous seriez capable d'utiliser du code tiers! Ne réinventez pas la roue. Surtout pas quand votre roue va être buggy, inefficace et plus difficile à utiliser. – jalf

+0

"un conteneur simple qui utilise le disque comme stockage" Nous avons déjà ces - ils sont appelés fichiers. Un itérateur n'est pas une interface si sensible à un fichier, mais si vous le souhaitez, la norme le prévoit également. –

Répondre

2

Si vous souhaitez utiliser l'héritage pour vos itérateurs, je vous recommande d'utiliser une approche différente de celle du STL begin()/end().

Jetez un coup d'œil sur IEnumerator à partir de .NET framework, par exemple.(MSDN documentation)

Vos classes de base peut ressembler à ceci:

class CollectionBase 
{ 
    // ... 
    virtual IteratorBase* createIterator() const = 0; 
}; 

class IteratorBase 
{ 
public: 
    virtual bool isEnd() const = 0; 
    virtual void next() const = 0; 
}; 

// usage: 
for (std::auto_ptr<IteratorBase> it = collection.createIterator(); !it->isEnd(); it->next) 
    { 
    // do something 
    } 

Si vous voulez rester avec begin()/end(), vous pouvez utiliser dynamic_cast pour vérifier que vous avez un bon type:

class MyIteratorBaseImpl 
{ 
public: 
    virtual bool operator!=(IteratorBase const &other) const 
    { 
     MyIteratorBaseImpl * other2 = dynamic_cast<MyIteratorBaseImpl*>(&other); 
     if (!other2) 
      return false; // other is not of our type 

     // now you can compare to other2 
    } 
} 
+1

Si vos performances importent vraiment dans votre cas, vous pouvez utiliser dynamic_cast dans la version de débogage et statique dans la version commerciale, comme ceci: assert (dynamic_cast (& other) == static_cast (& other)); – denisenkom

+0

Il y a une façon dont les itérateurs devraient fonctionner en C++, qui est begin()/end(). Il y a une manière dont les énumérations sont censées fonctionner dans Java, qui est hasMoreElements() et nextElement(). IEnumerators devrait fonctionner dans .NET, qui est isEnd()/next(). Tant que l'OP n'a pas déclaré qu'il voulait imiter Java ou .NET, mais pose une question C++ «simple», je trouve que recommander des concepts non-C++ serait au mieux trompeur. – DevSolar

0

Je peux vous conseiller d'ajouter à l'itérateur une fonction virtuelle 'entiy-id', et dans operator! = Vérifie this-> entity_id() et other.entity_id() (my example, la fonction 'position' est telle 'entité- id 'fonction).

4

Ce n'est pas la façon dont vous devriez utiliser C++. Je vous suggère fortement d'étudier les classes de conteneur de bibliothèque standard, telles que std :: vector et std :: map, et l'utilisation de templates. L'héritage devrait toujours être l'outil de conception de dernier recours.

1

S'il vous plaît do imiter la façon STL de faire des conteneurs. De cette façon, il serait possible, par exemple, de Utilisez <algorithm> avec vos conteneurs.

Questions connexes