Frage zu STL und Iterator
-
LordJaxom schrieb:
Sollten Ende-Iteratoren nicht eins hinter das letzte Element zeigen?

Ups, ja. Dann sind die beiden korrekt.
Aber dann kann ich ins Nirvana schreiben.
-
hi,
wie kann ich den iterator bis zum letzten element laufen lassen?
tree::treeiterator it = t.begin(); it != t.end(); it = it.next()class tree { private: class node *root; public: class treeiterator { private: std::stack<class node*> stack; class node *now; class node *end; class tree *ptree; bool left; bool right; public: treeiterator(class tree *tree_): now(NULL), left(false), right(false) { ptree = tree_; } void start_at_beginning() { now = ptree->get_root_node(); stack.push(now); } void start_at_end() { now = ptree->get_root_node(); std::stack<class node*> stack_tmp; while(true) { if(now->left) { stack_tmp.push(now->right); } if(now->right) { stack_tmp.push(now->left); } if (!stack_tmp.empty()) { now = stack_tmp.top(); stack_tmp.pop(); } else { break; } } } class node *get_node() { return now; } treeiterator &next() // next iterator for Depth first Search { if (now->left) { stack.push(now->right); } if (now->right) { stack.push(now->left); } if (!stack.empty()) { now = stack.top(); stack.pop(); } else { now = NULL; } return *this; } bool operator== (treeiterator &it) { return this->get_node() == it.get_node(); } bool operator!= (treeiterator &it) { return this->get_node() != it.get_node(); } node operator*() { return *get_node(); } }; public: tree(): root(NULL) {} class node *get_root_node() { return root; } class treeiterator begin() { class treeiterator it(this); it.start_at_beginning(); return it; } class treeiterator end() { class treeiterator it(this); it.start_at_end(); return it; } };for(tree::treeiterator it = t.begin(); it != t.end(); it = it.next()) { cout << it.get_node()->value; }
-
Default-Konstruktor sollte her, damit man so Dinger selber wieder in Arrays stopfen kann.
Ok. Könnte ich ja ganz einfach einbauen mit einem Default-Argument im Ctor.
Destruktor nicht virtual. Von sowas soll man nicht erben.
Ok. Was ist das Problem wenn man unnötigerweise virtual verwendet? Nur unnötiger Overhead durch die vtables oder wie die heißen oder auch sonst noch Probleme?
Was passiert, wenn in der Elementzuweisung im Kopierkonstruktor eine Exception fliegt?
Tja, gute Frage.
Wenn ich ein try catch mache, welches mir im catch Block dann den neuen Speicher wieder freigibt, und ich dann normal weitermache, so bekomm ich beim dtor ein Problem weil mir delete[] Speicher freigeben will denn es nicht mehr gibt!?
Was wäre die Lösung? Vielleicht Speicher=0 setzen nach delete[] und dies im Dtor nochmals prüfen bevor ich mit delete[] drüberfahre?safeArray(const safeArray& original) :m_count(original.m_count) { m_array=new T[m_count]; for(size_t i=0;i<m_count;i++) m_array[i]=original.m_array[i]; }print sollte '\n' statt std::endl benutzen.
OK.
**
sollte if(index>=0 && index<=m_refToSafeArray->m_count) nicht if(index>=0 && index<m_refToSafeArray->m_count) heißen?Ebenso return iterator(this,m_count); wohl return iterator(this,m_count-1);**
s.o. wegen end() = eins hinter letztem Element.
Naja, iter=end; iter=value; sollte ich ja verhindern, stimmt schon ("ins Nirvana schreiben").
Also wenn ich beim operator() merke, dass an einer Stelle dereferenziert wird, die nicht im gültigen Bereich liegt, raus mit Exception/assert?Aber was DU da fabrizierst, ist höchst bedenklich. Du verschleierst damit nur Programmierfehler bei der Array-Bedienung! Prüfen ist gut. Aber dann hart herausgehen. Am besten mit sowas wie assert.
Wie meinst du das genau?
Sobald also ein in irgendeiner Weise falscher Zugriff aufs safeArray erfolgt sofort Exception werfen? Viel "radikaler" mit falschen Zugriffen umgehen?
-
stl mal wieder schrieb:
Wie meinst du das genau?
Sobald also ein in irgendeiner Weise falscher Zugriff aufs safeArray erfolgt sofort Exception werfen? Viel "radikaler" mit falschen Zugriffen umgehen?Sogar assert statt exceptions. ~(Wobei ich ein heimlicher Freund von AssertExceptions bin.)~
safeArray(const size_t count) :m_count(count) { m_array=new T[m_count]; }const unsinnig
size_t schlecht, verbirgt minuszahlenfehlersafeArray(int count) :m_count(count) { assert(count>=0); m_array=new T[m_count]; }Auch beim
if(index>=0 && index<=m_refToSafeArray->m_count)kann sollte evtl aus diesem Gund index wohl ein int sein.
Ok, absolut unüblich für Arrays. Aber man sieht noch schneller, ob's eine Unter- oder Überschreitung war.Ja, das virtual macht nur ein Bißchen lahmer, schadet aber ansonsten gar nicht.
-
stl mal wieder schrieb:
Was wäre die Lösung? Vielleicht Speicher=0 setzen nach delete[] und dies im Dtor nochmals prüfen bevor ich mit delete[] drüberfahre?
Nee! Mach sowas nicht. Das ist am Ende auch nicht sicherer.
Überhaupt setze ich nie nach dem delete irgend einen Zeiger auf 0.Du könntest mit try/catch schauen, ob eine Exception geflogen ist, gegebenenfalls löschen und sie weiterwerfen.
Oder Du faßt den Speicher dort mit einem Smart-Pointer an, der im Exceptionfall löscht.
-
volkard schrieb:
size_t schlecht, verbirgt minuszahlenfehler
safeArray(int count) :m_count(count) { assert(count>=0); m_array=new T[m_count]; }Auch beim
if(index>=0 && index<=m_refToSafeArray->m_count)kann sollte evtl aus diesem Gund index wohl ein int sein.
Ok, absolut unüblich für Arrays. Aber man sieht noch schneller, ob's eine Unter- oder Überschreitung war.Nicht dein Ernst?
-
Ok. Was ist das Problem wenn man unnötigerweise virtual verwendet? Nur unnötiger Overhead durch die vtables oder wie die heißen oder auch sonst noch Probleme?
1. Technik:
ja, du erzeugst ne vtable, wo keine sein muss, und nimmst dem compiler damit evtl. auch Optimierungsmöglichkeiten.2. Philosophie:
(alle wissen es !? aber keiner weiss wo es steht ! ^^ also quasi ungeschriebenes Gesetz!) Du gibst anderen Nutzern mit einem virtuellen Dtor zu verstehen, das deine Klasse hervorragend zum Ableiten geeignet ist ... oder so ähnlich ^^ und genau das ist eigentlich nicht der Fall ... Das klasseninterface an sich ist ok, aber es ist alles andere als "gut zum Ableiten" geeignet
Deine Klasse ist als Implementation, also zum (wieder)verwenden designt, da ist Aggregation / Komposition Mittel der Wahl.
Polymorphie, sollte der Hauptanwendungsfall von Vererbung sein, also das austauschen von typen zur laufzeit (zur compilezeit sind Templates meist die bessere Wahl), und das willst du mit deiner klasse glaub ich nicht erreichen ^^Ciao ...
-
Naja, schwupps, habe ich eine Klasse PersistentesArray von safeArray abgeleitet, die als zuätzliches Attribut einen Dateinamen als String hat und im Konstruktor die Datei liest und im Destruktor die Datei schreibt.
Zu Aggregation habe ich keine Lust, weil ich das ganze Zugriff-Geraffele per Hand weiterleiten müßte.
Erbe ich normalerweise nicht von Containern, weil sie normalerweise keinen virtuellen Destruktor haben oder ist es umgegehrt?
-
@RHBaum: Vor allem Punkt 2 ist ein gutes Argument, danke für die Ausführungen.
@volkard: Nochwas zu den assert's:
iterator(const safeArray* refSafeArray, int index) :m_refToSafeArray(refSafeArray) { assert(index>=0 && index<=m_refToSafeArray->m_count); m_current=index; }Jetzt habe ich da einen Vergleich zwischen signed und unsigned.
Und: Woher weiß ich, dass size_t int und nicht long oder long long ist?
Oder ist size_t definitionsgemäß unsigned int?
-
stl mal wieder schrieb:
Jetzt habe ich da einen Vergleich zwischen signed und unsigned.
Und: Woher weiß ich, dass size_t int und nicht long oder long long ist?
Oder ist size_t definitionsgemäß unsigned int?Nee. int ist keineswegs richtig, sondern nur für Nubes eine Hilfe, die dauernd Fehler machen.
-
volkard schrieb:
Naja, schwupps, habe ich eine Klasse PersistentesArray von safeArray abgeleitet, die als zuätzliches Attribut einen Dateinamen als String hat und im Konstruktor die Datei liest und im Destruktor die Datei schreibt.
Die Klasse hat also zwei Aufgaben, die nichts miteinander zu tun haben.
volkard schrieb:
Erbe ich normalerweise nicht von Containern, weil sie normalerweise keinen virtuellen Destruktor haben oder ist es umgegehrt?
Weil es überhaupt keinen Sinn ergibt. Das hat nichts mit Destruktoren zu tun.
-
TyRoXx schrieb:
volkard schrieb:
Naja, schwupps, habe ich eine Klasse PersistentesArray von safeArray abgeleitet, die als zuätzliches Attribut einen Dateinamen als String hat und im Konstruktor die Datei liest und im Destruktor die Datei schreibt.
Die Klasse hat also zwei Aufgaben, die nichts miteinander zu tun haben.
Doch, die haben was mineinander zu tun.
Aber die Eigentliche Arbeit wird in safeArray und fstream erledigt.volkard schrieb:
Erbe ich normalerweise nicht von Containern, weil sie normalerweise keinen virtuellen Destruktor haben oder ist es umgegehrt?
Weil es überhaupt keinen Sinn ergibt. Das hat nichts mit Destruktoren zu tun.[/quote]
Doch, auch.
-
stl mal wieder schrieb:
safeArray& operator=(const safeArray& rhs) { safeArray temp(rhs); std::swap(m_array,temp.m_array); std::swap(m_count,temp.m_count); return *this; }Du kannst hier bei dem Copy-&-Swap Trick noch eine ggf unnötige Kopie sparen, indem Du dem Compiler das Kopieren überlässt, der das ggf wegoptimiert:
safeArray& operator=([b]safeArray temp[/b]) { this->swap(temp); // ruhig auslagern, das swap return *this; }