Я выставляю слишком много итераторов? - PullRequest
17 голосов
/ 11 марта 2011

В моем классе могут быть дети, поэтому мне нужно выставить итераторы. Класс рендеринга должен выполнить обратную итерацию, поэтому у меня есть обратные итераторы. Но есть ли способ получить их меньше, потому что это выглядит как много:

std::vector<AguiWidget*>::iterator          getChildBeginIterator();
std::vector<AguiWidget*>::reverse_iterator  getChildRBeginIterator();
std::vector<AguiWidget*>::iterator          getChildEndIterator();
std::vector<AguiWidget*>::reverse_iterator  getChildREndIterator();

std::vector<AguiWidget*>::const_iterator            getChildBeginIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator    getChildRBeginIterator() const;
std::vector<AguiWidget*>::const_iterator            getChildEndIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator    getChildREndIterator() const;

std::vector<AguiWidget*>::iterator          getPrivateChildBeginIterator();
std::vector<AguiWidget*>::reverse_iterator  getPrivateChildRBeginIterator();
std::vector<AguiWidget*>::iterator          getPrivateChildEndIterator();
std::vector<AguiWidget*>::reverse_iterator  getPrivateChildREndIterator();

std::vector<AguiWidget*>::const_iterator            getPrivateChildBeginIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator    getPrvateChildRBeginIterator() const;
std::vector<AguiWidget*>::const_iterator            getPrivateChildEndIterator() const;
std::vector<AguiWidget*>::const_reverse_iterator    getPrivateChildREndIterator() const;

Спасибо

Ответы [ 7 ]

13 голосов
/ 11 марта 2011

Те выглядят хорошо для меня. Или я не могу комментировать более точно, не зная, что именно вы делаете. Но одна вещь, которую вы можете наверняка сделать в любом случае: почему бы вам не использовать typedef? Если вы используете typedef, вы можете использовать его и вне класса, то есть в коде клиента, где вы будете использовать класс!

Например,

class sample
{
public:

  //make these typedef public so you use it from outside!
  typedef std::vector<AguiWidget*>::iterator  iterator ;
  typedef std::vector<AguiWidget*>::reverse_iterator reverse_iterator;
  typedef std::vector<AguiWidget*>::const_iterator const_iterator;
  typedef std::vector<AguiWidget*>::const_reverse_iterator const_reverse_iterator;

  iterator           child_begin();
  reverse_iterator   child_rbegin();
  iterator           child_end();
  reverse_iterator   child_rend();

  const_iterator            child_begin() const;
  const_reverse_iterator    child_rbegin() const;
  const_iterator            child_end() const;
  const_reverse_iterator    child_rend() const;
};

//Usage
sample s;
sample::iterator it= s.child_begin(); 
//see this ^^^^^^^^ how we use the typedef here!

Это выглядит лучше! В основном typedef инкапсулирует реализацию, потому что это помогает вам скрыть детали реализации класса; например, какой контейнер вы используете в классе, std::vector, std::list или что? Снова посмотрите иллюстрацию Использование выше; просто глядя на это, вы не можете сказать тип контейнера, не так ли?

Обратите внимание, что я также изменил имя функции. Я думаю, что это хорошо. В конце концов, STL использует только begin и end в отличие от beginIterator и endIterator. Тем не менее, мой вкус в нижнем регистре, вы все равно можете предпочесть последовательность в верхнем регистре!

По моему мнению, const функции не имеют особого смысла, вам, вероятно, понадобится следующий набор функций, если вы хотите выставлять итераторы только для чтения!

const_iterator            readonly_child_begin();
const_reverse_iterator    readonly_child_rbegin();
const_iterator            readonly_child_end();
const_reverse_iterator    readonly_child_rend();


//Usage
sample s;
sample::const_iterator cit= s.readonly_child_begin(); 
//see this ^^^^^^^^ how we use the typedef here!
2 голосов
/ 11 марта 2011

Простой способ разрезать этот интерфейс пополам - это использовать диапазоны вместо пар begin () / end (). Например, см. Boost's iterator_range . Или вы можете вернуть ссылку на контейнер, но тогда вы можете либо получить только const_iterator s, либо вы можете позволить внешнему изменить контейнер, чего вы, возможно, не хотите.

Обратите внимание, что векторные итераторы являются двунаправленными, поэтому вы можете снова наполовину сократить число методов, не раскрывая меньше функциональности. Если вам действительно нужно использовать ++ вместо --, вы можете использовать для этого фасад, например Boost's reverse_iterator .

Обратите внимание, что обе эти вещи (диапазоны и фасады reverse_iterator) концептуально просты, и вы можете легко построить их самостоятельно (хотя выполнение правильно , вероятно, требует существенной работы).

В любом случае, 4 функции вместо 16 звучат для меня очень разумно.

2 голосов
/ 11 марта 2011

Нет стандартного ответа, но задайте себе следующие вопросы:

  • Планируете ли вы использовать каждый из итераторов?
  • Вам нужна разница между ребенком?и частный ребенок?
  • Вам нужно предоставить что-то, что называется частным ребенком?
  • Разве индексирование не будет удобнее, чем итерация?

В противном случае этокажется совершенно в порядке;итераторы могут потребовать много кода в классе, но сделать его удобным для использования.(Вы должны увидеть мои полиморфные итераторы, поддерживающие XQuery, для коллекций XML, которые обертывают несколько разных не-STL-совместимых итераторов из сторонних библиотек ...)

Предложение typedef, выдвинутое несколькими комментаторами, являетсяхорошая идея, потому что она обеспечивает лучшую инкапсуляцию, даже если эта инкапсуляция не обеспечивается компилятором.

1 голос
/ 11 марта 2011

Этого можно достичь с помощью интерфейса mixin:

template<int Tag> class IteratorRange {
  private:
    std::vector<AguiWidget*>& range;
  public:
    IteratorRange(std::vector<AguiWidget*>& range) : range(range) {}

    typedef std::vector<AguiWidget*>::iterator  iterator ;
    typedef std::vector<AguiWidget*>::reverse_iterator reverse_iterator;
    typedef std::vector<AguiWidget*>::const_iterator const_iterator;
    typedef std::vector<AguiWidget*>::const_reverse_iterator const_reverse_iterator;

    iterator           begin();
    reverse_iterator   rbegin();
    iterator           end();
    reverse_iterator   rend();
};

enum { PublicChild, PrivateChild };
class MyClass : public IteratorRange<PublicChild>, private IteratorRange<PrivateChild> {
  MyClass() : IteratorRange<PublicChild>( ??? ), IteratorRange<PrivateChild>( ??? ) { }
  typedef IteratorRange<PublicChild> Public; // For MyClass::Public::begin etc
};
0 голосов
/ 11 марта 2011

Да, в основном.Стандартные библиотечные контейнеры тоже делают это.Добро пожаловать в C ++!

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

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

0 голосов
/ 11 марта 2011

Вы уже выставили std :: vector :: iterator, это означает, что вы уже выставили много деталей реализации.

Возвращает const std :: vector &, ссылка на const может уменьшить большую часть интерфейса и обеспечить больше функциональных возможностей.В противном случае в будущем вам может понадобиться добавить что-то вроде getChildSize (), getXXXSize () и т. Д.

0 голосов
/ 11 марта 2011

В зависимости от того, что вы делаете с этими итераторами, вы можете предоставлять некоторые функции посетителя:

template<class UnaryFunction>
void visit(UnaryFunction & f) {
    // apply function to each element in vector
}

Вы можете настроить эту подпись в соответствии с вашими потребностями, но это может сделать интерфейс более тонким.

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