Предоставить доступ к инкапсулированному контейнеру - PullRequest
5 голосов
/ 26 июля 2011
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;
};

Я ищу последовательный и чистый способ предоставления итераторов инкапсулированным контейнерам вызывающей стороне. Я выдвинул три идеи, отмеченные в исходном коде выше.

  1. Предоставляет size (), begin () и end (), все идеально подходит для доступа для чтения. Однако, поскольку возвращаемая ссылка Container является константой, вы сможете использовать только const_iterator. Возвращение ссылки неконстантной - это плохо, потому что сам контейнер может быть изменен (например, clear()).
  2. Предоставляет неконстантный доступ к элементам, однако нам часто требуется собственный метод size() (например, GetElementCount()). iterator::distance() можно использовать, но это может быть неэффективно для некоторых контейнеров (где operator++ / -- вызывается повторно для вычисления расстояния).
  3. Предоставляет пользовательскому итератору такие методы, как next() и т. Д. Тем не менее, требуется собственный метод size().

Я уверен, что есть более хорошие решения, поэтому, если вы их знаете, я буду рад их видеть.

Ответы [ 6 ]

2 голосов
/ 26 июля 2011

Я бы использовал эти имена вместо: iterator, const_iterator, begin, end, cbegin, cend и size() как:

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

И если вы можете использовать C ++ 0x, тогда используйте m_container.cbegin() и m_container.cend() как:

const_iterator cbegin() const { return m_container.cbegin(); }
const_iterator cend() const { return m_container.cend(); }
2 голосов
/ 26 июля 2011

Сочетание (2) и (3), вероятно, будет тем, что я бы сделал:пока те, которые предоставлены контейнером, в порядке, они могут быть использованы.

1 голос
/ 26 июля 2011

Конечно, самое простое это:

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

    Container m_container;
};

но это делает ваш class X устаревшим.

Кроме того, если вам действительно нравится ваш класс, добавьте следующие методы:

Container::const_iterator ElementBegin() const;
Container::const_iterator ElementEnd() const;
int size() const;
1 голос
/ 26 июля 2011

Я не могу придумать много более чистых методов;вы могли бы рассмотреть облегченное (4) решение, предоставляющее доступ с помощью

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

. Я бы предпочел (3), поскольку тип контейнера полностью инкапсулируется, т. е. ваш тип не обязательно требует включения, и вы можете изменить контейнертип без перекомпиляции зависимых модулей.

0 голосов
/ 26 июля 2011

Если вы можете использовать Boost, для вас есть библиотека: http://www.boost.org/doc/libs/1_47_0/libs/iterator/doc/index.html В частности, посмотрите на iterator_facade и iterator_adapter

Вот пример того, что нужно сделать - он предоставляет держатель std :: vector с STL-совместимыми итераторами. Вы можете расширить его, добавив другие методы, такие как operator [], size (), push_back () и т. Д.

шаблон класс VectorHolder { общественности: typedef T value_type;

общественность: VectorHolder () : m_values ​​() { }

общественность: typedef имя типа std :: vector :: iterator vector_iterator; typedef имя типа 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 голосов
/ 26 июля 2011

2 и 3 действительно не являются разными вариантами.Тем не менее, 3, как написано, в значительной степени бесполезно.Ни один алгоритм STL не будет использовать CustomIterator::next.Для совместимости с STL вы должны написать:

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

и указать CustomIterator стандарт operator++ и operator*

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...