2016-09-20 6 views
-1

Wie schreibe ich eine Basisklasse und mehrere abgeleitete Iteratorklassen?Iterator Vererbung und Vererbung * Dies

Muss der Iterator sich selbst zurückgeben (* dies)?

Bisher verwende ich typename X und static_cast<X&>(*this), damit die abgeleitete Klasse eine Funktion erben kann, die sich von der Basisklasse zurückgibt.

This sieht hässlich aus. Gibt es einen besseren Weg?

Simplified Code:

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

Die Alternative ist, über die Verwendung von Vererbung zu vergessen, weniger Code zu schreiben. Kopieren Sie einfach die Funktion, die *this zurückgibt, und fügen Sie sie in die abgeleiteten Klassen ein.

Diese Alternative zu mir zunehmend akzeptabel scheint ...

+1

auch 'ContiguousIterator' hat zwei unabhängige öffentliche' T * p' Mitglieder Chaos. –

+0

Ich wusste nicht crtp. Würde Crtp in diesem Fall helfen? – rxu

+0

einfache Lösung: Ändern Sie T * P zu privat. Kann nicht in "geschützt" geändert werden. irgendwie. Ich habe es schon mal versucht. – rxu

Antwort

1

Allgemeinen virtual ist eine Menge Aufwand für so etwas wie Iteratoren die leicht sein sollte. Der übliche Weg ist CRTP. Welches ist ein wenig kompliziert, aber sieht wie folgt aus:

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

Die Basis dauert in der Regel den abgeleiteten Typ, und was auch immer braucht es für die Funktion Signaturen als Template-Parameter. Dann fügen Sie zwei self() Funktionen hinzu, die Ihnen den abgeleiteten Typ geben, was bedeutet, dass Sie eigentlich virtual nicht benötigen. Alles wird vom Compiler trivial eingezeichnet. (Man beachte, ich habe auch eine vernünftige Konstruktor gegeben und machte 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) {} 
} ; 

Dann wird der abgeleiteten Typ einfach als normale erbt, außer Sie this-> verwenden müssen, um das Element zugreifen zu können, da die Eltern eine Vorlage sehen sie es, hier zu arbeiten.. http://coliru.stacked-crooked.com/a/81182d994c7edea7

Dieses eine hocheffiziente Iterator erzeugt, ohne Overhead ich glaube, dass diese Technik sehr stark Iterator Bibliothek in Boost-Jahren verwendet wird: http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/#new-style-iterators

+0

Mein Gott. Ich würde nicht wissen, virtuell geben Sie einen erheblichen Overhead für Iteratoren. http://programmers.stackexchange.com/questions/191637/in-c-why-and-how-are-virtual-functions-slower Vielen Dank für die Antwort. – rxu

+0

@rxu: Da Sie die Basis nicht verwenden, fügt virtuell wahrscheinlich keinen Overhead für Ihren Fall hinzu. Aber das garantiert nicht. –

+0

Ist es eine gute Idee, einen virtuellen Destruktor für die Basisklasse zu deklarieren? Wird dies einen Overhead verursachen? – rxu