代码之家  ›  专栏  ›  技术社区  ›  Statement

如何在不公开所用容器的情况下公开迭代器?

  •  23
  • Statement  · 技术社区  · 16 年前

    我使用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<t>::迭代器吗?我希望只依赖于迭代器,而不是实现容器…

    4 回复  |  直到 16 年前
        1
  •  18
  •   jonner    16 年前

    您可能会发现以下文章很有趣,因为它正好解决了您发布的问题: On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It

        2
  •  2
  •   Jeroen Dirks    16 年前

    我以前做过以下工作,所以得到了一个独立于容器的迭代器。这可能是杀伤力过大,因为我还可以使用调用方传入的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;
        }
    }
    
        3
  •  1
  •   Sébastien RoccaSerra    16 年前

    这应该是您想要的:

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

    Accelerated C++ :

    每当你有一个类型,比如 vector<T> ,这取决于模板参数,并且您希望使用该类型的成员,例如 size_type ,这本身就是一个类型,必须在整个名称之前 typename 让实现知道将名称视为类型。

        4
  •  1
  •   PierreBdR    16 年前

    我不确定您所说的“不公开std::vector”是什么意思,但实际上,您可以这样定义typedef:

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

    稍后您将能够更改这些typedef,而不会让用户注意到任何内容…

    顺便说一句,如果您希望类表现为容器,还公开一些其他类型被认为是一种良好的实践:

    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 documentation on vectors

    编辑:添加 typename 正如评论中建议的那样