2016-09-20 3 views
-1

Как написать базовый класс и несколько производных классов итератора?Итератор Наследование и наследование * this

Должен ли итератор возвращаться сам (* это)?

До сих пор я использовал typename X и static_cast<X&>(*this), чтобы производный класс мог наследовать функцию, возвращающуюся из базового класса.

This выглядит уродливым. Есть ли способ лучше?

упрощенный код:

#include <iterator> 
#include <iostream> 
template <typename T, typename X> 
class BaseIterator : public std::iterator<std::input_iterator_tag, T> { 
    //Not intended to be used directly. 
    private: 
     T* p; 
    protected: 
     virtual void increment(void)=0; 
     virtual T* stride_index(int index)=0; 
    public: 
     virtual ~BaseIterator(){} //virtual destructor. 
     X operator++(int) { //takes a dummy int argument 
      X tmp(static_cast<X&>(*this)); 
      increment(); 
      return tmp; 
     } 
     bool operator==(const X & rhs) { return p==rhs.p; } 
} ; 
template <typename T> 
class ContiguousIterator : public BaseIterator<T, ContiguousIterator<T> > { 
    private: 
     T* p; 
    protected: 
     inline void increment(void) {++p;} 
     inline T* stride_index(int index){return p + index;} 
    public: 
     virtual ~ContiguousIterator(){} //destructor. 
     ContiguousIterator(T* x) :p(x) {} 
     ContiguousIterator(const ContiguousIterator<T> & mit) : p(mit.p) {} 
} ; 

int main(void){ 
    int i[]={0,1,2,3,4,5}; 
    ContiguousIterator<int> itbegin(i); 
    ContiguousIterator<int> it(i); 
    it++; 
    std::cout << "result: " << (it == itbegin) << std::endl; 
} 

В качестве альтернативы можно забыть об использовании наследования писать меньше кода. Просто скопируйте и вставьте функцию, возвращающую *this в производные классы.

Эта альтернатива кажется более приемлемым для меня ...

+1

Кроме того, 'ContiguousIterator' имеет два несвязанных общественные' T * p' members. Это просто хаос. –

+0

Я не знал crtp. Помогло бы crtp в этом случае? – rxu

+0

простой исправление: изменение T * p частное. Невозможно изменить его на защищенный. некоторые как. Раньше я пытался. – rxu

ответ

1

Вообще, virtual много накладных расходов на что-то вроде итераторов, который должен быть легким. Обычный способ - это CRTP. Что немного сложнее, но выглядит следующим образом:

template <typename T, typename X> 
class BaseIterator : public std::iterator<std::input_iterator_tag, T> { 
    protected: 
     T* p; 
     X* self() {return static_cast<X*>(this);} 
     const X* self() const {return static_cast<const X*>(this);} 
     BaseIterator(T* x) :p(x) {} 
    public: 
     X operator++(int) { //takes a dummy int argument 
      X tmp(*self()); 
      self()->increment(); 
      return tmp; 
     } 
     bool operator==(const X & rhs) const { return p==rhs.p; } 
} ; 

Основание обычно имеет производный тип, плюс все, что нужно для функции подписей в качестве параметров шаблона. Затем вы добавляете две функции self(), которые дают вам производный тип, что означает, что вам действительно не нужно virtual. Компилятор все тривиально. (Обратите внимание, я также дал ему здравомыслящий конструктор, и сделал operator==const.

template <typename T> 
class ContiguousIterator : public BaseIterator<T, ContiguousIterator<T> > { 
    public: 
     typedef BaseIterator<T, ContiguousIterator<T> > parent; 
     void increment(void) {++(this->p);} 
     T* stride_index(int index) const {return this->p + index;} 
    public: 
     virtual ~ContiguousIterator(){} //destructor. 
     ContiguousIterator(T* x) :parent(x) {} 
     ContiguousIterator(const ContiguousIterator<T> & mit) : parent(mit.p) {} 
} ; 

Тогда производный тип просто наследует, как обычно, за исключением того, вы должны использовать this-> для доступа к члену, так как родительский шаблон См. это работает здесь:. http://coliru.stacked-crooked.com/a/81182d994c7edea7

Это создает высокоэффективную итератор, без дополнительных затрат Я считаю этот метод используется довольно сильно в библиотеке итератора Boost в: http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/#new-style-iterators

+0

Мой бог. Я бы не знал, что виртуальные дают значительные накладные расходы для итераторов. http://programmers.stackexchange.com/questions/191637/in-c-why-and-how-are-virtual-functions-slower Большое спасибо за ответ. – rxu

+0

@rxu: Учитывая, что вы не используете базу, виртуальный, вероятно, не добавляет никаких накладных расходов для вашего дела. Но этого не гарантирует. –

+0

Можно ли объявить виртуальный деструктор для базового класса? Поможет ли это накладные расходы? – rxu

Смежные вопросы