Was tun wenns nix zu löschen gibt?
-
@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".
-
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
Itemvorhanden sein soll, die Precondition vonRemoveItemist. 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 dervalue_typeder 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 auchstd::list<Item>::removetut.
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 einassert(false)aufrufen, wenn man anstd::list<X>::eraseoderstd::vector<X>::erasefalsche Iteratoren übergibt.Und es gibt wohl auch noch andere Möglichkeiten, wie man diese
RemoveItemFunktion 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
Itemvorhanden sein soll, die Precondition vonRemoveItemist. 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 dervalue_typeder 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 auchstd::list<Item>::removetut.
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 einassert(false)aufrufen, wenn man anstd::list<X>::eraseoderstd::vector<X>::erasefalsche Iteratoren übergibt.Und es gibt wohl auch noch andere Möglichkeiten, wie man diese
RemoveItemFunktion 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.