开发者

Give access to encapsulated container

class X {
  public:
    typedef std::list<int> Container;

    // (1)
    const Container& GetElements() const;

    // (2开发者_运维技巧)
    Container::iterator ElementBegin();
    Container::iterator ElementEnd();

    // (3)
    CustomIterator GetElementIterator();

  private:
    Container m_container;
};

I'm looking for a consistent and clean way of providing iterators to encapsulated containers to the caller. I came up with the three ideas marked in the source code above.

  1. Provides size(), begin() and end(), all perfect for read access. However, because the returned Container reference is const, you'll only be able to use const_iterator. Returning the reference non-const is bad, because the container itself could be modified (e.g. clear()).
  2. Provides non-const access to the elements, however we'd often need an own size() method (like GetElementCount()). iterator::distance() could be used, but that may be inefficient for some containers (where operator++/-- is called repeatedly to calculate the distance).
  3. Provides a custom iterator with methods like next() etc. Still an own size() method is required.

I highly bet there're nicer solutions, so if you know any, I'd be glad to see them.


A mix of (2) and (3) would probably be what I'd do :

class X {
  public :
    typedef std::list<int> ElementContainer;
    typedef ElementContainer::size_type ElementSizeType;
    typedef ElementContainer::iterator ElementIterator;
    typedef ElementContainer::const_iterator ConstElementIterator;

    ElementIterator elementBegin() { return m_container.begin(); }
    ElementIterator elementEnd() { return m_container.end(); }

    ConstElementIterator elementBegin() const { return m_container.begin(); }
    ConstElementIterator elementEnd() const { return m_container.end(); }

    ElementSizeType elementSize() const { return m_container.size(); }

  private :
    ElementContainer m_container;
};

It still leaves room to write custom iterators (by changing the typedefs), but as long as the ones provided by the container are ok, they can be used.


I would use these names instead : iterator, const_iterator, begin, end, cbegin, cend and size() as:

class X 
{
  public :

    typedef std::list<int>::iterator iterator;
    typedef std::list<int>::const_iterator const_iterator ;

    iterator begin() { return m_container.begin(); }
    iterator end() { return m_container.end(); }

    const_iterator cbegin() const { return m_container.begin(); }
    const_iterator cend() const { return m_container.end(); }

    size_t size() const { return m_container.size(); }

  private :
    std::list<int> m_container;
};

And if you can use C++0x, then use m_container.cbegin() and m_container.cend() as:

const_iterator cbegin() const { return m_container.cbegin(); }
const_iterator cend() const { return m_container.cend(); }


I can't think of much cleaner methods; you might consider the lightweight (4) solution giving access with

const Container& container() const { return m_container; }

I would prefer (3) since the container type becomes fully encapsulated, i.e. your type does not necessarily require inclusion of and you can change the container type without recompiling depending modules.


Off course, the simplest is this :

class X {
  public:
    typedef std::list<int> Container;

    Container m_container;
};

but that makes your class X obsolete.

Other then that, if you really like your class, then add next methods :

Container::const_iterator ElementBegin() const;
Container::const_iterator ElementEnd() const;
int size() const;


2 and 3 really aren't distinct options. However, 3 as written is pretty much useless. No STL algorithm will use CustomIterator::next. For STL compatibility, you'd write:

// mix of 2 and 3
CustomIterator begin();
CustomIterator end();

and give CustomIterator the standard operator++ and operator*


If you can use Boost, there is a library for you: http://www.boost.org/doc/libs/1_47_0/libs/iterator/doc/index.html Specifically look at iterator_facade and iterator_adapter

Here is an example of what to do - It provides an std::vector holder, with STL compatible iterators. You can extend it by adding other methods, like operator[], size(), push_back() etc.

template class VectorHolder { public: typedef T value_type;

public: VectorHolder() : m_values() { }

public: typedef typename std::vector::iterator vector_iterator; typedef typename std::vector::const_iterator vector_const_iterator;

class iterator : public boost::iterator_adaptor<iterator, vector_iterator>
{
public:
    iterator()
        : iterator::iterator_adaptor_()
    {
    }

    iterator(const vector_iterator& it)
        : iterator::iterator_adaptor_(it)
    {
    }

private:
    friend class boost::iterator_core_access;
};

class const_iterator : public boost::iterator_adaptor<const_iterator, vector_const_iterator>
{
public:
    const_iterator()
        : const_iterator::iterator_adaptor_()
    {
    }

    const_iterator(const vector_const_iterator& it)
        : const_iterator::iterator_adaptor_(it)
    {
    }

    const_iterator(const iterator& it)
        : const_iterator::iterator_adaptor_(it.base())
    {
    }
private:
    friend class boost::iterator_core_access;
};


iterator begin()
{
    return iterator(m_values.begin());
}

iterator end()
{
    return iterator(m_values.end());
}

const_iterator begin() const
{
    return const_iterator(m_values.begin());
}

const_iterator end() const
{
    return const_iterator(m_values.end());
}protected:
std::vector<T> m_values;};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜