Iterator (Iterator als geschachtelte Klasse)
-
Hallo,
ich habe folgendes Programm://--------------main.cpp--------------------------- #include <iostream.h> #include "istack1.h" main() { Istack s; //Behaelter-Definition for (int j=0; j<10; j++) s.push(j); Istack::iterator i; //Iterator-Definition for (i=s.start( ); i!=s.ende( ); ++i) cout<<*i<<" "; getchar(); } //-----------------------------------------Ergebnis // 0 1 2 3 4 -DS ist ein Feld//----------------istack1.h------------------------- typedef int T; class Istack { T* erste; int anz; int len; public: class iterator { T* c; //Zeiger auf aktuelles Element public: iterator(T* cur=NULL):c(cur){} bool operator!=(iterator i)const{return c!=i.c;} T& operator*()const{return *c;} iterator operator++(){ ++c; return *this;} }; Istack():anz(0),len(5){erste=new T[len];} iterator start(){ return erste;} //<--- iterator ende(){ return erste+anz;} void push(const T& ee) { if(len==anz) return; *(erste+anz)=ee; anz++; } }; //--------------------------------------------------Wenn ich folgendes...
iterator start(){ return erste;}durch das...
iterator start(){return iterator(erste);}ersetzte, ist das ja das gleiche, wie ich nach ausprobieren festgestellt habe.
Aber warum ist das eigentlich so?
Ich würde nämlich sagen, dass ersteres...iterator start(){ return erste;}fogendes zurück gibt:
return iterator ersteAber das würde den Konstruktor...
iterator(T* cur=NULL):c(cur){}nicht aufrufen, weil das ja ohne Klammern ist und somit nicht der Form entsprechen würde.
-
Da findet eine sogenannte implizite Konvertierung statt. Wenn ein Objekt von einem bestimmten Typ erwartet wird, aber ein Objekt von einem anderen Typ gegeben wird UND ein passender Konstruktor vorhanden ist, dann wird dieser Konstruktor benutzt um ein passendes Objekt zu erzeugen.
Falls man dieses Verhalten nicht wünscht, muss man den entsprechenden Konstruktor explicit machen. Welche der beiden Verhaltensweisen sinnvoller ist, hängt vom jeweiligen Anwendungsfall ab.
-
c++ user schrieb:
Ich würde nämlich sagen, dass ersteres...
iterator start(){ return erste;}fogendes zurück gibt:
return iterator ersteWas würde denn dann zweiteres zurückgeben?
return iterator iterator(erste);?
Tatsächlich passiert in etwa sowas:// return erste --> iterator tmp(erste); // bzw: iterator tmp = erste return tmp; // return iterator(erste) --> iterator tmp(iterator(erste)); // bzw: iterator tmp = iterator(erste) return tmp;
-
was mir beim drübergucken aufgefallen ist:
iterator operator++(){ ++c; return *this;}sicher, dass du das möchtest?
es macht zwar in den meisten beispielen (auch in deinem) keinen unterschied, aber z.bsp.
++++iteratorverhält sich nicht so, wie du das erwartest.
normalerweise gibt man beim prä-increment-operator eine referenz zurück:iterator& operator++(){ ++c; return *this;}deine einrückung ist übrigens nicht all zu hübsch:
{ T* erste; int anz; int len;void push(const T& ee) { if(len==anz) return; *(erste+anz)=ee; anz++; }hier passiert nicht nur etwas (aus anwendersicht) unerwartetes, sondern es ist noch dazu auch nicht wirklich leserlich:
void push(const T& ee) { if(len == anz) return; //immernoch doof // *(erste+anz) = ee; erste[anz] = ee; //übersichtlicher, meinst du nicht auch? anz++; }die unteren beiden zeilen könnte man auch noch zusammenfassen:
erste[anz++] = ee
würde ich aber nicht machen, nur damit ich es auch mal geschrieben habezu deinem iterator-konstruktor:
ich würde ihn zwar nicht explicit machen, aber eigentlich private und dafür die container-klasse als friend hinzufügen, sonst würde man so was hier machen dürfen:int x; Istack::iterator iter(&x); ++iter; std::cout << *iter << std::endl; //bumm!bb
-
Wow ihr seid ja schnell mit dem Antworten.
Vielen Dank!
-
c++ user schrieb:
Hallo,
ich habe folgendes Programm://--------------main.cpp--------------------------- #include <iostream.h> #include "istack1.h" main() //...Und auf 3 Zeilen gleich 2 dicke Fehler:
<iostream.h> ist seit ca. 12 Jahren veraltet. Das ist im Bereich Computersprachen ungefähr so veraltet wie ne Dampfmaschine. Der Header heißt <iostream>. ohne .h am Ende.
main() ist nichtmal veraltet, das war noch nie gültiges C++. main hat schon immer einen Rückgabetypen namens int. Das gabs mal in C - ist aber auch seit 1999 nicht mehr aktuell.
-
Noch ein heißer Tipp, da ich es erst jetzt sehe:
typedef int T;Gehe ich Recht in der Annahme, dass du dies gemacht hat, um gegebenenfalls den Typ in deinem Stack einfach ändern zu können, ohne dabei überall im Code int durch den neuen Typ zu ersetzen?
Falls ja: C++ kennt da eine Technik die sogar noch besser funktioniert. Templates! Die sind genau dafür da.
Ich habe mal das gleiche Programm nochmal mit Templates geschrieben und auch einige der oben genannten Verbesserungen zugefügt (bis auf den privaten Konstruktor für den Iterator. Das fände ich unintuitiv.
#include <cstddef> // Für Makro "NULL" template <typename T, int len=5> class Istack { private: T* erste; int anz; public: class iterator { private: T* c; public: iterator(T* cur=NULL):c(cur){} bool operator!=(iterator i)const{return c!=i.c;} T& operator*()const{return *c;} iterator& operator++(){ ++c; return *this;} }; Istack():anz(0){erste=new T[len];} iterator start(){ return erste;} iterator ende(){ return erste+anz;} void push(const T& ee) { if(len==anz) return; erste[anz] = ee; ++anz; } }; // ... #include <iostream> using namespace std; int main() { Istack<int> s; // Hier sagen, welchen Typ und Länge man will. 5 ist Defaultlänge. for (int j=0; j<10; j++) s.push(j); Istack<int>::iterator i; // Hier natürlich gleichen Typ und Länge wählen for (i=s.start( ); i!=s.ende( ); ++i) cout<<*i<<" "; }