2017-05-11 2 views
0
#include <iostream> 
using namespace std; 

template <typename T> class Iterator 
{ 

T * ptr; 

public: 

    Iterator(T * addr) 
    { 
     ptr=NULL; 
     ptr=addr; 
    } 

    //not working 
    //virtual Iterator * operator ++(int x); 


    /*Iterator * operator ++(int x) 
    { 
     (this->ptr)++; 
     return this; 
    }*/ 

    T operator *() 
    { 
     return *ptr; 
    } 
}; 

template<typename T> 
class Vector{ 

T * a; 
public: 
    Vector(size_t n) 
    { 
     a=new T[5]; 
    } 
    T& operator [](int x) 
    { 
     return a[x]; 
    } 
    class iterator: public Iterator<T> 
    { 
     public: 

      iterator(T * addr): Iterator<T>(addr) 
      {} 

      /* not working 
      Iterator<T> * operator ++(int x) 
      { 
       Iterator<T>::ptr++; 
       return this; 
      }*/ 


      //working now 
      iterator * operator ++(int x) 
      { 
       Iterator<T>::ptr++; 
       return this; 
      } 
    }; 
    iterator begin() 
    { 
     iterator ob(&a[0]); 
     return ob; 
    } 
}; 

int main() 
{ 

Vector <char> v(5); 

for(int i=0;i<5;i++) 
    v[i]=i+65; 

for(int i=0;i<5;i++) 
    cout<<v[i]<<endl; 

/*~~~~~~ What I want~~~~~~ 

Iterator <char> p=v.begin();  

*/ 

// what is working now: 

Vector<char>:: iterator p=v.begin(); 

while(*p){ 
    cout<<*p<<endl; 
    p++; 
    } 

    return 0; 
} 

In dem obigen Code, ich möchte Operator machen ++() in Iterator-Klasse, virtuelle, so dass Haupt in die ich verwenden kann:Implementieren von Java-Sammlung Hierarchie in C++

Iterator <char> ptr=v.begin(); 

anstatt zu Verwendung:

Vector <char>:: iterator p=v.begin(); 

, die eine einzige Basisklasse refernce mit Laufzeit-Polymorphismus ist, um zu bestimmen, welchen Betreiber auf anrufen ++ je nachdem, ob der Wert ein Vektor oder eine Liste ist. Wenn ich es jedoch in der Iterator-Klasse als virtuell deklariere, funktioniert es nicht. Ich muss es ausschließlich für die inneren Klassen deklarieren, aber ich möchte eine einzige Schnittstelle wie in Java. Ich versuche, die Java-Sammlungshierarchie in C++ für bessere Struktur zu implementieren. Was läuft falsch? Ist es passiert, weil Iterator ist eine Vorlage-Klasse, die Rückgabetyp Iterator auch Vorlage, das heißt, der Operator ++() -Funktion eine Vorlagenfunktion? Ich weiß, Template-Funktionen können nicht virtuell sein, nur konkrete Funktionen können virtuell sein. Was kann getan werden, um dies zu beheben?

+0

Warum wollen Sie Laufzeitpolymorphismus auf Iterator? Was ist die tatsächliche Verwendung? –

+0

In Java haben wir eine einzelne Iterator-Schnittstelle, aber in C++ verwenden wir Vektor :: Iterator und einen anderen für Liste und so weiter. Also wollte ich eine einzelne Iterator-Basisklasse erstellen. Verwenden Sie dann eine Basisklassenreferenz, um verschiedene Iteratorimplementierungen aufzurufen, z. B. hat die Liste eine andere Implementierung als der Vektor. Zur Laufzeit würde es herausfinden, wessen Iteratorimplementierung aufgerufen werden soll, abhängig davon, auf welches Objekt die "Iterator" -Referenz zeigt. –

+0

Sie antworten mir nicht. Ich weiß bereits, dass Sie Laufzeit-Polymorphismus wollen. Was ich gefragt habe, ist warum. Wie im eigentlichen Anwendungsfall. Ich frage mich, wie besonders Ihr Anwendungsfall ist, da der konzeptbasierte Kompilier-Polymorphismus, den wir täglich anwenden, bereits die meisten Fälle abdeckt. –

Antwort

-1

Ihr Code fast arbeiten. Ich hatte nur drei Probleme darin zu beheben:

  • ptr sollte anstelle von privaten in Iterator Klasse geschützt werden, wenn Sie in der Lage sein wollen, dass es für den Zugriff auf in überschriebenen Methoden einer abgeleiteten Klasse
  • der Inkrementierung Betreiber sollte virtual Iterator<T> ...
  • Wenn Sie Ihr Array bis zu einem Nullwert iterieren möchten, sollten Sie am Ende einen Nullwert schreiben.

So stellt dies ohne Vorwarnung und gut laufen mit Clang 3.4.1:

#include <iostream> 
using namespace std; 

template <typename T> class Iterator 
{ 
protected: 
T * ptr; 

public: 

    Iterator(T * addr) 
    { 
     ptr=NULL; 
     ptr=addr; 
    } 

    //not working - in fact works perfectly 
    virtual Iterator<T> * operator ++(int x) 
    { 
     (this->ptr)++; 
     return this; 
    } 

    T operator *() 
    { 
     return *ptr; 
    } 
}; 

template<typename T> 
class Vector{ 

T * a; 
public: 
    Vector(size_t n) 
    { 
     a=new T[5]; 
    } 
    T& operator [](int x) 
    { 
     return a[x]; 
    } 
    class iterator: public Iterator<T> 
    { 
     public: 

      iterator(T * addr): Iterator<T>(addr) 
      {} 

      /* not working 
      Iterator<T> * operator ++(int x) 
      { 
       Iterator<T>::ptr++; 
       return this; 
      }*/ 


      //working now but useless as we can use the base class version 
      /*iterator * operator ++(int x) 
      { 
       Iterator<T>::ptr++; 
       return this; 
      }*/ 
    }; 
    iterator begin() 
    { 
     iterator ob(&a[0]); 
     return ob; 
    } 
}; 

int main() 
{ 

Vector <char> v(5); 

for(int i=0;i<5;i++) 
    v[i]=i+65; 

for(int i=0;i<5;i++) 
    cout<<v[i]<<endl; 

v[5] = 0; // set a null at the end of the vector 

/*~~~~~~ What I want~~~~~~ and that works...*/ 

Iterator <char> p=v.begin();  



// what is working now: 

//Vector<char>:: iterator p=v.begin(); 

while(*p){ 
    cout<<*p<<endl; 
    p++; 
    } 

    return 0; 
} 

Sie können die überschriebenen Version von operator ++ in Vector-Klasse Kommentar- und es funktioniert auch gut.

0

Virtuelle Funktionen können nur über einen Zeiger aufgerufen werden. Ihre begin Funktion gibt keinen Zeiger zurück. Wenn Sie das beheben, werden Sie viele Kompilierungsfehler im Zusammenhang mit Typkonvertierungen bekommen.

Sie können auch einen Verweis stattdessen verwenden, aber es sollte ein r-Referenzwert sein:

Iterator<char> &&p = v.begin(); 
+0

Zeiger * oder * Referenz –

+0

@LaurentG Ja, Referenz funktioniert auch und es ist einfacher, den aktuellen Code zu ändern, um es zu verwenden. Ich habe die Antwort aktualisiert. – Zefick

0

Hier ist Beispielimplementierung mit der richtigen postfix Zuwachs Version:

template<typename T> 

class Iterator 
{ 
    private: 
    T* ptr; 
    public: 
    Iterator(T* ptr1):ptr(ptr1) 
    { 
    } 
    virtual Iterator operator ++(int) 
    { 
     Iterator result(*this); 
     (this->ptr)++; 
     return result; 
    } 
    T operator*() 
    { 
     return *ptr; 
    } 
}; 

template<typename T> 
class Vector 
{ 
    private: 
    T* data; 
    public: 
    Vector() 
    { 
     data = new T(100); 
     for(int i=0;i<100;i++) 
     data[i]=i; 
    } 


Iterator<T> begin() 
{ 
    return Iterator<T>(&(data[0])); 
} 
}; 
0

Lassen Sie mich vorweg sagen, Ihre Implementierung wird nicht so funktionieren, wie Sie es beabsichtigt haben. Auch die anderen Antworten werden derzeit nicht gesehen.

Wenn ein Zeiger als Implementierung eines polymorphen Iterators zum Scheitern verurteilt ist, ändert polymorphphsm nicht die Tatsache, dass Sie alle konkreten Iteratoren implementieren müssen, die polymorph sind.

Ähnlich wie Java (ich nehme einige Vertrautheit an), wird eine globale polymorphe Iterator ermöglicht mit einer Schnittstelle möglich. Jetzt haben C++ keine dedizierten Schnittstellen, sondern reine virtuelle Klassen.

template<typename T> 
class Iterator 
{ 
public: 
    virtual Iterator& operator++() = 0; // pure virtual 
    // other methods 
}; 

template<typename T> 
class Vector 
{ 
    class Iterator // Vector's concrete Iterator 
     : public ::Iterator<T> // analogous to implements in Java 
    { 
    public: 
     Iterator& operator++() override { /* implementation */ } 
     // other methods... 
    }; 
}; 

template<typename T> 
class List 
{ 
    class Iterator // List's concrete Iterator 
     : public ::Iterator<T> // implements 
    { 
    public: 
     Iterator& operator++() override { /* implementation */ } 
     // other methods... 
    }; 
}; 

Da Polymorphismus gilt nur für Referenzen und Zeiger und Referenzen in C++ explizit geschrieben werden muss

Vector<int> v; 
List<int> l; 
Iterator<int>&& rit = v.begin(); 
const Iterator<int>& crit = l.begin(); 

ist, wie Sie Iterator verwenden würden.

Und als letzter Hinweis ist Polymorphie ein Werkzeug, Missbrauch ist schlimmer als nicht. Berücksichtigen Sie die Leistungseinbußen und wiegen Sie die Flexibilität aus.

+0

Vielen Dank für die Antwort. Die letzten zwei Zeilen über die Aufführung, die du erwähnt hast, kannst du bitte über die Leistungsstrafen berichten? Ich bin neu in C++ - Programmierung, ich habe nur C++ verwendet, um Datenstrukturen zu codieren, aber nie C++ verwendet, um etwas Kompakteres als eine Einheit zu machen. Dies ist also mein erster Versuch. Könntest du bitte mehr über die erwähnten Leistungseinbußen sagen? Vielen Dank .. –

+0

@DeepayanGhosh [This] (http://stackoverflow.com/questions/113830/performance-penalty-for-working-with-interfaces-in-c) und [dies] (http: // stackoverflow. com/questions/449827/virtual-functions-and-performance-c) sind gute Ausgangspunkte. Es hat bereits umfangreiche Diskussionen gegeben, ich werde nicht auf die Details eingehen. Was ich sagen werde, ist, dass ein Behälter seinen Bedürfnissen entsprechen sollte, der oft breit ist, da er templatiert ist. –

+0

Danke für die Hilfe. Ich muss viel von diesen Fragen lernen. Vielen Dank –