Auswahl const-Funktion vs. Non-const-Funktion
-
Hallo, gut im neuen Jahr gestartet?

Ich hätte gleich wieder eine Frage. Momentan arbeite ich mich ein wenig tiefer in Iteratoren ein, dabei habe ich einen kleinen Container geschrieben.
const_iteratorist dabei eine Klasse, die Iteratorfunktionalität bereitstellt, und bei der die Dereferenzierung Const-Zeiger/-Referenzen zurückgibt. Die Klasseiteratorist davon abgeleitet und überschreibt die Dereferenzierungsoperatoren, sodass die referenzierte Objekte veränderbar sind.Nun habe ich für den Container diverse Methoden bereitgestellt, einige davon in einer
const- und einer normalen Version. Als Beispiel:iterator end(); const_iterator end() const;Da ich nun die Vererbungshierarchie habe, wird die eine Funktion gar nie aufgerufen -
iteratorist implizit inconst_iteratorkonvertierbar.Dasselbe passiert auch beim Indexoperator, allerdings hier aus einem anderen Grund. Ich habe wieder zwei Deklarationen:
reference operator[] (size_type index); const_reference operator[] (size_type index) const;Selbst wenn ich das Objekt nicht verändere, also beispielsweise einen der folgenden Aufrufe habe, wird die nicht-konstante Version aufgerufen.
int a = container[3]; const int& b = container[3];Woran liegt das? Ich dachte, es werde automatisch die Version gesucht, bei der möglichst viele Qualifizierer vorhanden sind. Oder wie macht man das richtig?
Edit: Ich hab mir dazu gerade noch was überlegt. Könnte es sein, dass man die beiden Versionen der Memberfunktionen nur schreibt, um auch bei konstanten Containern gewisse Operationen zu erlauben?
-
Nexus schrieb:
Edit: Ich hab mir dazu gerade noch was überlegt. Könnte es sein, dass man die beiden Versionen der Memberfunktionen nur schreibt, um auch bei konstanten Containern gewisse Operationen zu erlauben?
Genau. Wenn es die const-Varianten der Memberfunktionen nicht gäbe, dann wären konstante Container ja praktisch wertlos, da es nichts gäbe, was man damit noch machen könnte.
-
Okay, danke für die Bestätigung. Ich hatte drum noch im Hinterkopf, dass die
const-Varianten hier noch einen anderen Zweck hätten... Aber scheinbar nicht.
-
Nexus schrieb:
[...] habe ich einen kleinen Container geschrieben.
const_iteratorist dabei eine Klasse, die Iteratorfunktionalität bereitstellt, und bei der die Dereferenzierung Const-Zeiger/-Referenzen zurückgibt. Die Klasseiteratorist davon abgeleitet und überschreibt die Dereferenzierungsoperatoren, sodass die referenzierte Objekte veränderbar sind.
Ein iteratorist also einconst_iterator? (Oder verwendest du keine public-Vererbung?)Felix
-
iterator und const_iterator sind nicht verwandt. das bedeutet dass man hier doppelten Code schreiben muss, oder aber boost verwenden: http://www.boost.org/doc/libs/1_37_0/libs/iterator/doc/index.html#iterator-facade-and-adaptor
-
Ich habe auch schon gehört, dass man das besser nicht tun soll. Aber die Standardbibliothek macht es ja auch so, zumindest in meiner Implementierung (MSVC++ 2008 Express):
template<class _Ty, class _Alloc> class _Vector_iterator : public _Vector_const_iterator<_Ty, _Alloc> { // ... }Was ist denn daran so schlecht?
-
Würde mich jetzt schon noch interessieren, was gegen die Vererbung spräche, zumal der Standard es bei mir auch so macht. Klar, von der Logik her ist es nicht sehr intuitiv (
iteratorist einconst_iterator), aber andererseits besitzt einiteratordie gesamte Funktionalität einesconst_iterators und noch darüber hinaus.
-
Du kannst die iteratoren ploetzlich polymorph verwenden.
Die wirkliche Frage sollte eher sein:
warum will ich hier Vererbung haben?Die Antwort ist: weil es mir schreibarbeit erspart.
Und damit sollte das Thema erledigt sein. Denn "weil es mir schreibarbeit erspart" ist meistens ein guter Hinweis auf einen Designfehler.
Vererbung ist eine enorm starke Bindung die ich nur eingehen will, wenn es mir einen wirklichen Vorteil bringt. Aber die Boost Iterator Facade/Adapter Sachen ersparen mir Kopplung und Schreibarbeit im Vergleich zu der Vererbungsvariante.
Der wirkliche Grund warum viele Standard Libraries iteratoren so implementieren ist weil es einfach und standard konform ist. Aber ist es deshalb gut?
-
Shade Of Mine schrieb:
Der wirkliche Grund warum viele Standard Libraries iteratoren so implementieren ist weil es einfach und standard konform ist. Aber ist es deshalb gut?
Ja, weil es unnötige Codeduplizierung vermeidet, und damit entsprechende Copy and Paste Fehler minimiert.
-
und ich dachte immer, öffnetliche erblichkeit würde IST EIN bedeuten.
da fällt mir nur http://www.c-plusplus.net/forum/viewtopic-var-t-is-75672-and-highlight-is-bebel.html zu ein.
-
volkard schrieb:
und ich dachte immer, öffnetliche erblichkeit würde IST EIN bedeuten.
da fällt mir nur http://www.c-plusplus.net/forum/viewtopic-var-t-is-75672-and-highlight-is-bebel.html zu ein.Private Vererbung wäre vielleicht angebrachter.
-
Tachyon schrieb:
Shade Of Mine schrieb:
Der wirkliche Grund warum viele Standard Libraries iteratoren so implementieren ist weil es einfach und standard konform ist. Aber ist es deshalb gut?
Ja, weil es unnötige Codeduplizierung vermeidet, und damit entsprechende Copy and Paste Fehler minimiert.
Und die Nachteile ignorieren wir einfach?
natuerlich ist es bequem. aber bequem ist meistens falsch.
schau dir bitte boost iterator facade/adapter an. du schreibst einen bruchteil von code den du mit der vererbungsvariante brauchst und bist deutlich fehlerfreier.
wenn du privat vererbst umgehst du zwar die probleme hast aber wieder viel schreibarbeit. und dann wuerde ich sowieso zu layering raten, du hast ein iterator objekt dass alle operationen anbietet und jeweils eine facade fuer iterator und const_iterator drueber...
aber die richtige wahl ist dennoch boost...
-
volkard schrieb:
da fällt mir nur http://www.c-plusplus.net/forum/viewtopic-var-t-is-75672-and-highlight-is-bebel.html zu ein.
Nettes Geschichtlein.

Ich werde mir wahrscheinlich mal Boost.Iterator anschauen. Danke für die Antworten!

-
mammamia;