Как я могу выставить итераторы, не раскрывая используемый контейнер? - PullRequest
23 голосов
/ 01 октября 2008

Я уже некоторое время пользуюсь C #, и возвращение к C ++ - головная боль. Я пытаюсь перенести некоторые мои практики с C # на C ++, но я испытываю некоторое сопротивление и буду рад принять вашу помощь.

Я бы хотел выставить итератор для такого класса:

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;
};

Я пытаюсь что-то, что не является проблемой? Должен ли я просто ввести typedef std :: vector :: iterator? Я надеюсь, что зависит только от итератора, а не от контейнера реализации ...

Ответы [ 4 ]

18 голосов
/ 01 октября 2008

Вы можете найти следующую статью интересной, так как она посвящена именно той проблеме, которую вы опубликовали: О противоречии между объектно-ориентированным и общим программированием в C ++ и о том, что может с этим стереть тип

2 голосов
/ 01 октября 2008

Ранее я делал следующее, чтобы получить итератор, независимый от контейнера. Это могло быть излишним, так как я мог бы также использовать API, в котором вызывающая сторона передает vector<T*>&, который должен быть заполнен всеми элементами, а затем вызывающая сторона может просто выполнить итерацию непосредственно из вектора.

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;
    }
}
1 голос
/ 01 октября 2008

Я не уверен насчет того, что вы имеете в виду под "не публичным раскрытием std :: vector", но на самом деле вы можете просто определить свой typedef следующим образом:

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

Вы сможете изменить эти typedefs позже, и пользователь ничего не заметит ...

Кстати, хорошей практикой также является предоставление нескольких других типов, если вы хотите, чтобы ваш класс вел себя как контейнер:

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;

А если нужно вашему классу:

 typedef typename std::vector<T>::const_pointer const_pointer;
 typedef typename std::vector<T>::const_reference const_reference;

Значение всех этих typedef вы найдете здесь: Документация STL по векторам

Редактировать: добавлено typename, как предлагается в комментариях

1 голос
/ 01 октября 2008

Это должно делать то, что вы хотите:

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

С Ускоренный C ++ :

Когда у вас есть тип, такой как vector<T>, который зависит от параметра шаблона, и вы хотите использовать элемент этого типа, например size_type, который сам является типом, вы должны предшествовать всему имя typename, чтобы реализация знала, что имя следует рассматривать как тип.

...