Was tun wenns nix zu löschen gibt?



  • Nexus schrieb:

    @ hustbaer:
    Meinst du zufällig das von Stepanov?

    Jopp, genau das, danke!
    Geht los ab Seite 134. Bzw. bei "Lecture 13. Iterators".

    Habs zwar nicht vollständig gelesen und bin mir dementsprechend auch nicht sicher, ob die von dir erwähnte Thematik dort angesprochen wird, aber das wurde hier zumindest ein paar Mal genannt.

    Lies es (ganz) 😉
    Ich kann zwar mit einigem was er schreibt nicht wahnsinnig viel Anfangen, aber alles in allem finde ich es ziemlich gut. Wenig theoretisches lala sondern relativ schön aus der Praxis. Manchmal übertreibt er es für meinen Geschmack ein wenig, aber wenn man das so liest, bekommt man finde ich eine gute Vorstellung davon, was man so beim Thema Library-Design alles falsch oder richtig machen kann 🙂



  • hustbaer schrieb:

    Lies es (ganz) 😉
    Ich kann zwar mit einigem was er schreibt nicht wahnsinnig viel Anfangen, aber alles in allem finde ich es ziemlich gut. Wenig theoretisches lala sondern relativ schön aus der Praxis. Manchmal übertreibt er es für meinen Geschmack ein wenig, aber wenn man das so liest, bekommt man finde ich eine gute Vorstellung davon, was man so beim Thema Library-Design alles falsch oder richtig machen kann 🙂

    Ja, werde ich tun, danke!

    Ich habe mich bisher ein wenig mit dem dort erwähnten swap() und dem drumherum befasst, aber das ist auch schon eine Weile her. Das hat mich da sehr interessiert, aber irgendwie hab ichs wohl wieder vernachlässigt. Naja, in nächster Zeit habe ich mir sowieso vorgenommen, mich theoretisch ein wenig weiterzubilden, da kommt das gerade recht... 😉



  • @DStefan:
    Guck mal.
    Erstmal vorweg: ich persönlich finde die "return bool" Variante die "beste". Nur damit wir uns nicht misverstehen. Hab ich zwar schon geschrieben, aber vielleicht hast du das ja überlesen.

    Davon abgesehen bin ich aber der Meinung, dass der Standpunkt "remove(gibtsNicht) == Fehler" seine Berechtigung hat.

    Es ist logisch gesehen halt einfach nicht möglich, etwas zu entfernen, was nicht da ist.

    Die Frage ob "remove(gibtsNicht)" "OK" ist, ist IMO äquivalent zur Frage ob "delete NULL" "OK" ist. Der C++ Standard sagt in beiden Fällen "ja, ist OK". Was aber durchaus strittig ist, in beiden Fällen (hallo volkard :)).

    "delete p", wenn "p" NULL ist, ist, meiner Meinung nach, logisch betrachtet Unfug. Praktisch, aber Unfug. Warum die "Hardliner" (hallo volkard :D) argumentieren dass es verboten werden sollte: du kannst ein Objekt welches nicht existiert, nicht löschen. Der Versuch es zu tun, deutet darauf hin, dass das Programm welches es tut, nicht weiss was es tut. = Programmierfehler.

    Und von diesem Standpunkt aus betrachtet, macht IMO keine andere Definition von "remove" Sinn, als die, dass es ein Fehler ist, "remove" mit einem Item aufzurufen, welches garnicht in der Collection enthalten ist.

    ----

    Bitte, ich habe nicht mehr Verhalten vorgeschlagen, sondern weniger. Ich wollte nicht mehr Aufwand treiben, sondern weniger.

    Ansichtssache. Aus Sicht der Implementierung: ja, vielleicht. Aus Sicht der Interface-Definition: nein. "Entfernt das Element x, falls es vorhanden ist" ist "mehr" als "Entfernt das Element x".


  • Administrator

    DStefan schrieb:

    Nein, es ist z.B. auch die Frage, ob eine Klassen macht, was sie soll. Eine Klasse (meine ich) soll ihre Pre- und Postconditions eisern schützen.

    Gut und hustbaer wie auch volkard sagen, dass das Item vorhanden sein soll, die Precondition von RemoveItem ist. Und wenn die Precondition verletzt ist, dann soll man dem User dies schliesslich mitteilen, da man die Pre- und Postconditions eisern schützen muss, nicht? 🙂

    Die Frage ist halt wirklich nur, ob man diese Precondition annehmen soll oder nicht?
    Und da denke ich, kommt es sehr auf die Art der Funktion an. Ist zum Beispiel Item der value_type der Containerklasse, dann kann man wohl davon ausgehen, dass die Precondition nicht gilt, da diese Funktion einfach alle Vorkommnisse von diesem Item-Wert löschen soll. Ähnlich wie dies auch std::list<Item>::remove tut.
    Ist RemoveItem hier allerdings zum Beispiel eine interne Funktion der Klasse ( private ) und Item ein Container-Element, dann sollte man wohl die Precondition schon prüfen, ob Item auch wirklich zu diesem Container gehört. Dies könnte man auch auf eine öffentliche Funktion, welche einen Iterator annimmt verwenden. Wie dies durchaus gewisse Implementationen machen, nämlich ein assert(false) aufrufen, wenn man an std::list<X>::erase oder std::vector<X>::erase falsche Iteratoren übergibt.

    Und es gibt wohl auch noch andere Möglichkeiten, wie man diese RemoveItem Funktion verstehen kann 😉

    Wenn man allerdings die Art wählt, dass man keine Precondition nimmt, dann würde ich auch einen entsprechenden Rückgabewert empfehlen, aus dem einfachen Grund, dass man dem User dann helfen kann, sowas zu machen:

    bool result = removeItem(item);
    assert(!result);
    

    Eine Library soll schliesslich auch Userfreundlich sein, also diesem helfen wo es nur geht.

    Grüssli



  • Dravere schrieb:

    DStefan schrieb:

    Nein, es ist z.B. auch die Frage, ob eine Klassen macht, was sie soll. Eine Klasse (meine ich) soll ihre Pre- und Postconditions eisern schützen.

    Gut und hustbaer wie auch volkard sagen, dass das Item vorhanden sein soll, die Precondition von RemoveItem ist. Und wenn die Precondition verletzt ist, dann soll man dem User dies schliesslich mitteilen, da man die Pre- und Postconditions eisern schützen muss, nicht? 🙂

    Die Frage ist halt wirklich nur, ob man diese Precondition annehmen soll oder nicht?
    Und da denke ich, kommt es sehr auf die Art der Funktion an. Ist zum Beispiel Item der value_type der Containerklasse, dann kann man wohl davon ausgehen, dass die Precondition nicht gilt, da diese Funktion einfach alle Vorkommnisse von diesem Item-Wert löschen soll. Ähnlich wie dies auch std::list<Item>::remove tut.
    Ist RemoveItem hier allerdings zum Beispiel eine interne Funktion der Klasse ( private ) und Item ein Container-Element, dann sollte man wohl die Precondition schon prüfen, ob Item auch wirklich zu diesem Container gehört. Dies könnte man auch auf eine öffentliche Funktion, welche einen Iterator annimmt verwenden. Wie dies durchaus gewisse Implementationen machen, nämlich ein assert(false) aufrufen, wenn man an std::list<X>::erase oder std::vector<X>::erase falsche Iteratoren übergibt.

    Und es gibt wohl auch noch andere Möglichkeiten, wie man diese RemoveItem Funktion verstehen kann 😉

    Wenn man allerdings die Art wählt, dass man keine Precondition nimmt, dann würde ich auch einen entsprechenden Rückgabewert empfehlen, aus dem einfachen Grund, dass man dem User dann helfen kann, sowas zu machen:

    bool result = removeItem(item);
    assert(!result);
    

    Eine Library soll schliesslich auch Userfreundlich sein, also diesem helfen wo es nur geht.

    Grüssli

    Damit bin ich auf der ganzen Linie einverstanden. Ich bin seit dem ersten Posting davon ausgegangen, dass removeItem() ein Teil der öffentlichen Schnittstelle ist. Und ja, falls removeItem() fordert, dass das übergebene Item vorhanden ist (also eine solche Precondition festlegt), dann sollte es eine Exception geben - oder meinetwegen auch ein assert().

    Bloß bin ich eben auch davon ausgegangen, dass diese Diskussion hier sich um die Frage dreht ob dies eine Precondition sein sollte. Ich dachte eigentlich, das sei klar zu sehen gewesen, aber vielleicht habe ich mich nicht gut genug ausgedrückt.

    Der Vorschlag mit der Rückgabe eines bool liegt in sofern auf meiner Linie, als dies dem User die Möglichkeit gibt, seinerseits auf Programmierfehler zu prüfen - oder eben auch nicht. Nicht die Klasse definiert das Nichtvorhandensein des Items als Fehler, sondern der Kunde. Und ich meine, das ist die richtige Stelle dafür.

    Stefan.



  • hustbaer schrieb:

    Die Frage ob "remove(gibtsNicht)" "OK" ist, ist IMO äquivalent zur Frage ob "delete NULL" "OK" ist. Der C++ Standard sagt in beiden Fällen "ja, ist OK". Was aber durchaus strittig ist, in beiden Fällen (hallo volkard :)).

    "delete p", wenn "p" NULL ist, ist, meiner Meinung nach, logisch betrachtet Unfug. Praktisch, aber Unfug. Warum die "Hardliner" (hallo volkard :D) argumentieren dass es verboten werden sollte: du kannst ein Objekt welches nicht existiert, nicht löschen. Der Versuch es zu tun, deutet darauf hin, dass das Programm welches es tut, nicht weiss was es tut. = Programmierfehler.

    Nun ja - vermutlich kann man sich darüber wirklich den Mund fusselig diskutieren 😉 Vielleicht verstehen wir auch unter "Programmierfehler" verschiedenes. Beziehungsweise wir beantworten die Frage verschieden, wer definieren soll, was ein Fehler ist und was nicht.

    Dein Punkt ist, dass man ein Objekt, das es nicht gibt, nicht löschen kann. Meiner ist, das man es nicht zu löschen braucht - um zu dem "versprochenen" Ergebnis zu kommen, dass das Objekt nach dem Aufruf nicht mehr existiert.

    In diesem Sinne stimme ich deinem letzten Satz nicht zu. removeItem(gibtsNicht) und delete 0 deuten in meinen Augen nicht auf einen Fehler hin. Sie stellen lediglich einen bestimmten Status her, nämlich das es das Objekt nach dem Aufruf nicht mehr gibt.

    Stefan.



  • hustbaer schrieb:

    Ansichtssache. Aus Sicht der Implementierung: ja, vielleicht. Aus Sicht der Interface-Definition: nein. "Entfernt das Element x, falls es vorhanden ist" ist "mehr" als "Entfernt das Element x".

    Außerdem ist "Entfernt das Element x" schneller.

    Ach, da kommt mir ja gerade, daß ich in den letzten Tagen viel über pop() nachgedacht habe, die haben die Tendenz, sehr einfach zu sein.

    void popFront(){
    		++begin;
    	}
    

    und es wäre wirklich eine Ungeheuerlichkeit, da ein if reinzumachen. Widersteht dem Drang, aus C++ ein zweites Java zu machen. Zero abstraction overhead ist die Devise.

    auch assert, siehe Seite 41.
    http://www.boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/08/iterators-must-go.pdf



  • volkard schrieb:

    hustbaer schrieb:

    Ansichtssache. Aus Sicht der Implementierung: ja, vielleicht. Aus Sicht der Interface-Definition: nein. "Entfernt das Element x, falls es vorhanden ist" ist "mehr" als "Entfernt das Element x".

    Außerdem ist "Entfernt das Element x" schneller.

    Im Fall "Suchen, Finden, Löschen", um den es hier geht, nicht. Bzw. nur in recht esoterischen Spezialfällen.

    BTW: Liest sich interessant. Muss man sehen ob's ein Video von dem Vortrag gibt. (Hast du vielleicht nen Link?)



  • hustbaer schrieb:

    Außerdem ist "Entfernt das Element x" schneller.

    Im Fall "Suchen, Finden, Löschen", um den es hier geht, nicht. Bzw. nur in recht esoterischen Spezialfällen.

    da=suche()
    if(gefunden(da))
       lösche(da);
    

    das if fällt jedesmal weg, unabhängig davon, wie suchen und löschen geht.

    hustbaer schrieb:

    BTW: Liest sich interessant. Muss man sehen ob's ein Video von dem Vortrag gibt. (Hast du vielleicht nen Link?)

    Hab keinen Link.



  • volkard schrieb:

    da=suche()
    if(gefunden(da))
       lösche(da);
    

    das if fällt jedesmal weg, unabhängig davon, wie suchen und löschen geht.

    Das stimmt so nicht. Man kann immer

    lösche(suche(item))
    

    schreiben, weil lösche() das Argument ohnehin gegen end() testen muss, um korrekt zu arbeiten. Und suche(item) wird doch wohl end() liefern müssen, falls item nicht gefunden wird. Die obige Vorgehensweise ist also genau die, die man braucht, wenn man nicht darauf reagieren möchte, dass item nicht vorhanden ist.

    Hingegen muss man zum Abfangen dieses "Programmierfehlers" schreiben:

    da = suche(item);
    assert(da != end());
    lösche(da);
    

    Und das ist mehr Aufwand.

    Oder habe ich jetzt irgend etwas übersehen?

    Stefan.



  • DStefan schrieb:

    volkard schrieb:

    da=suche()
    if(gefunden(da))
       lösche(da);
    

    das if fällt jedesmal weg, unabhängig davon, wie suchen und löschen geht.

    Das stimmt so nicht. Man kann immer

    lösche(suche(item))
    

    schreiben, weil lösche() das Argument ohnehin gegen end() testen muss, um korrekt zu arbeiten. Und suche(item) wird doch wohl end() liefern müssen, falls item nicht gefunden wird. Die obige Vorgehensweise ist also genau die, die man braucht, wenn man nicht darauf reagieren möchte, dass item nicht vorhanden ist.

    Hingegen muss man zum Abfangen dieses "Programmierfehlers" schreiben:

    da = suche(item);
    assert(da != end());
    lösche(da);
    

    Und das ist mehr Aufwand.

    Oder habe ich jetzt irgend etwas übersehen?

    Natürlich.
    lösche muß doch nicht gegen end testen!



  • @volkard:
    Achherrjeh, ja, natürlich fälle ein "if" weg.
    Es fällt aber nichts weg, was wirklich ins Gewicht fallen würde.

    Ich meine ein "if" wird, im Vergleich dazu wie lange "suche()" braucht, ziemlich untergehen.


Anmelden zum Antworten