2008-10-01 9 views
22

J'utilise C# depuis un moment maintenant, et retourner en C++ est un casse-tête. J'essaie d'obtenir certaines de mes pratiques de C# avec moi en C++, mais je trouve une certaine résistance et je serais heureux d'accepter votre aide.Comment puis-je exposer les itérateurs sans exposer le conteneur utilisé?

Je voudrais exposer un itérateur pour une classe comme ceci:

template <class T> 
class MyContainer 
{ 
public: 
    // Here is the problem: 
    // typedef for MyIterator without exposing std::vector publicly? 

    MyIterator Begin() { return mHiddenContainerImpl.begin(); } 
    MyIterator End() { return mHiddenContainerImpl.end(); } 

private: 
    std::vector<T> mHiddenContainerImpl; 
}; 

Suis-je essayer quelque chose qui est pas un problème? Dois-je juste typedef std :: vector < T> :: iterator? J'espère que le tout selon le iterator, pas le contenant ... la mise en œuvre

+0

Voir aussi cette [question] (http://stackoverflow.com/questions/127009/returning-an-any-kind-of-input-iterator-instead-of-a-vectoriterator-or-a -listit) –

Répondre

2

Cela devrait faire ce que vous voulez:

typedef typename std::vector<T>::iterator MyIterator; 

De Accelerated C++:

Chaque fois que vous avez un type, comme vector<T>, cela dépend d'un paramètre de modèle, et vous voulez utiliser un membre de ce type, tel que size_type, qui est lui-même un type, vous devez précéder le nom entier par typename pour laisser l'implémentation savoir pour traiter le nom comme un type.

1

Je ne suis pas sûr de ce que vous entendez par « ne pas exposer std :: vecteur public » mais bien, vous pouvez simplement définir votre typedef comme ça:

typedef typename std::vector<T>::iterator iterator; 
typedef typename std::vector<T>::const_iterator const_iterator; // To work with constant references 

Vous serez en mesure de changer ces typedefs plus tard, sans que l'utilisateur ... quoi que ce soit remarqué

Par ailleurs, il est considéré comme une bonne pratique d'exposer aussi quelques autres types si vous voulez que votre classe se comporte comme un conteneur:

typedef typename std::vector<T>::size_type size_type; 
typedef typename std::vector<T>::difference_type difference_type; 
typedef typename std::vector<T>::pointer pointer; 
typedef typename std::vector<T>::reference reference; 

Et si nécessaire par votre classe:

Vous trouverez la signification de tous ces typedef est ici: STL documentation on vectors

Edit: Ajout de la typename comme suggéré dans les commentaires

+0

Peut-être que je suis un peu exagéré, mais je voudrais avoir le typedef juste pour exposer que j'utilise stl itérateurs, pas le conteneur réel. Si je fais typedef std :: vector :: iterator itérateur, alors les gens peuvent juste faire std :: vector <ínt> :: iterator iter = example.Begin() ;. (suite) – Statement

+0

Bien que cela ne semble pas être un problème au début, imaginez si j'ai changé l'implémentation interne de ma classe pour utiliser une liste à la place.Le code client serait cassé. L'utilisation d'un itérateur commun qui fonctionne avec de nombreux conteneurs différents résoudrait ce problème. Le problème est que je n'ai trouvé aucun moyen de le faire. – Statement

+0

Petite correction à votre message: puisque T est un paramètre de template, vous devez utiliser le mot-clé typename dans votre typedefs, c'est-à-dire typedef typename std :: vector :: iterator iterator; –

2

J'ai déjà fait ce qui suit J'ai eu un itérateur indépendant du conteneur. Cela a peut-être été excessif puisque j'aurais pu aussi utiliser une API où l'appelant passe dans un vector<T*>& qui devrait être rempli avec tous les éléments et ensuite l'appelant peut juste itérer directement depuis le vecteur.

template <class T> 
class IterImpl 
{ 
public: 
    virtual T* next() = 0; 
}; 

template <class T> 
class Iter 
{ 
public: 
    Iter(IterImpl<T>* pImpl):mpImpl(pImpl) {}; 
    Iter(Iter<T>& rIter):mpImpl(pImpl) 
    { 
     rIter.mpImpl = 0; // take ownership 
    } 
    ~Iter() { 
     delete mpImpl; // does nothing if it is 0 
    } 
    T* next() { 
    return mpImpl->next(); 
    } 
private: 
    IterImpl<T>* mpImpl; 
}; 

template <class C, class T> 
class IterImplStl : public IterImpl<T> 
{ 
public: 
    IterImplStl(C& rC) 
    :mrC(rC), 
    curr(rC.begin()) 
    {} 
    virtual T* next() 
    { 
    if (curr == mrC.end()) return 0; 
    typename T* pResult = &*curr; 
    ++curr; 
    return pResult; 
    } 
private: 
    C& mrC; 
    typename C::iterator curr; 
}; 


class Widget; 

// in the base clase we do not need to include widget 
class TestBase 
{ 
public: 
    virtual Iter<Widget> getIter() = 0; 
}; 


#include <vector> 

class Widget 
{ 
public: 
    int px; 
    int py; 
}; 

class Test : public TestBase 
{ 
public: 
    typedef std::vector<Widget> WidgetVec; 

    virtual Iter<Widget> getIter() { 
     return Iter<Widget>(new IterImplStl<WidgetVec, Widget>(mVec)); 
     } 

    void add(int px, int py) 
    { 
     mVec.push_back(Widget()); 
     mVec.back().px = px; 
     mVec.back().py = py; 
    } 
private: 
    WidgetVec mVec; 
}; 


void testFn() 
{ 
    Test t; 
    t.add(3, 4); 
    t.add(2, 5); 

    TestBase* tB = &t; 
    Iter<Widget> iter = tB->getIter(); 
    Widget* pW; 
    while (pW = iter.next()) 
    { 
     std::cout << "px: " << pW->px << " py: " << pW->py << std::endl; 
    } 
} 
+0

OK mais pas un itérateur standard. Je préfère ne pas réinventer la roue. D'autres sont déjà censés être habitués aux itérateurs standards, pas de courbe d'apprentissage là-bas. –

Questions connexes