container mit nicht-kopierbaren elementen
-
Das Ding ist was vom Standard garantiert ist.
Okay, hier auf deinem Rechner ist Vector vielleicht mit einem virtuellen Destruktor ausgestattet. Dann schreibst du Code der das nutzt oder sogar darauf baut. 2 Wochen später willst du den Code auf deiner Waschmaschine oder meinem Atomkraftwerk nutzen, der Compiler und seine STL dort implementieren den Standard aber strikter, haben demzufolge keinen Vector mit virtuellem Destruktor. Was passiert? Richtig, ein zweites Tschernobyl bzw deine Waschmaschine färbt deine Wäche pink.
Ein anderer Punkt ist sauberes Design:
Listen, Vectoren oder Arrays sind Utilities. Sie haben nichts mit irgendwas fachlichem zu tun, nie in keinster Weise.
Durch das Ableiten von diesen spezialisierst du sie ja und bringst fachliches Wissen rein. Schreibst du dir einen eigenen Vector für Pointer ist das Ok. Schreibst du dir aber einen Vector für dein nicht kopierbares Objekt, dann ist das schon sehr speziell.Umgekehrt brauchen die Standardcontainer auch gewisse Vorbedingungen erfüllt damit sie ordentlich arbeiten können (genau wie dein eigener Container). Erfüll sie und du brauchst nix eigenes schreiben.
-
Skym0sh0 schrieb:
Richtig, ein zweites Tschernobyl
Ein drittes

-
Nein, nein, auch ich habe hier keinen virtuellen Destruktor. Ich bin nur der Meinung, dass ich den auch nicht benötige, so lange ich nicht mit Basisklassenzeigern herumhantiere.
Und wenn ich mir einen Vector für Pointer durch Ableitung von std::Vector schreibe, dann ist der selbstverständlich speziell, limitiert (aber meiner Meinung nach nicht limitierter als vector - auch den sollte ich wohl nicht durch Verwaltung eines Basisklassenzeigers verwalten), und vor allen Dingen will ich diese Klasse ja nicht in einer Library oder sonstwie zur weiteren Verwendung anbieten ...
-
Belli schrieb:
Nein, nein, auch ich habe hier keinen virtuellen Destruktor. Ich bin nur der Meinung, dass ich den auch nicht benötige, so lange ich nicht mit Basisklassenzeigern herumhantiere.
Das ist richtig. Abe wenn du dir so einen Container für Pointer schreibst und ihn dann natürlich oft benutzt, wächst die Gefahr, doch etwas falsch zu machen. Wenn mehrere Personen an einem Porjekt beteiligt sind, wird es noch gefährlicher. Dabei kann man das gleiche auch ganz einfach haben:
std::vector<std::unique_ptr<MeineKlasse>> v;Oder, besser noch, man macht seine Klasse movable.
-
Belli schrieb:
An die Boost-Kenner:
So weit ich weiß, gibt es dort sowas wie einen Pointer-Vector. Wie wird das dort gemacht? Ohne von std::vector zu erben?Bei Boost wird, wie es sein sollte, der unterliegende vector als Member gehalten. Und alles reimplementiert, da bei einem ptr_vector ohnehin praktisch jede Methode reimplementiert werden muss. Was ein starkes Zeichen dafür ist, dass hier Vererbung nicht das richtige Mittel ist. Siehe Auto und Motor. Bloß weil ein paar Methoden gleich heißen (und ein paar wenige auch identisch sind), macht das einen Motor noch nicht zum Auto.
Womit wir beim nächsten Thema sind: Vererbung würde den ptr_vector/non_copyable_vector selbst zu einem vector machen.std::vector<void*> foo; boost::ptr_vector<int> bar; foo = bar;wäre dann möglich. Ebenso mit den nicht-kopierbaren Objekten. Aber man kann Container, die so etwas modellieren, eben nicht einfach als normalen Vektor auffassen, denn das würde all das kaputt machen, was diese Container eigentlich erreichen wollen. Das Liskovsche Substitutionsprinzip wird also verletzt. Das wiegt viel schwerer als ein fehlender virtueller Destruktor. Der virtuelle Destruktor ist eine technische Einzelheit, das was ich gerade beschreibe heißt, das diese Modellierung an sich falsch wäre.
Als Alternative werfe ich mal private-Vererbung in den Raum. Die bringt die oben genannten Probleme nicht mit (ist keine "ist ein"-Relation), erlaubt aber die einfache "Wiederverwertung" vorhandener Methoden.
-
Zu diesem Prinzip (welches ich nicht kenne/kannte) finde ich bei Wikipedia:
Das liskovsche Substitutionsprinzip (LSP) oder Ersetzbarkeitsprinzip ist ein Kriterium in der objektorientierten Programmierung, das die Bedingungen zur Modellierung eines Datentyps für seinen Untertyp angibt. Es besagt, dass ein Programm, das Objekte einer Basisklasse T verwendet, auch mit Objekten der davon abgeleiteten Klasse S korrekt funktionieren muss, ohne dabei das Programm zu verändern.
Nun wäre meine abgeleitete Klasse ja
class ptrvector : public vector<meinTyp*> {}und ich würde Objekte der Klasse vector<meinTyp*> gar nicht benutzen, so dass streng genommen das oben aufgeführte Prinzip gar nicht verletzt werden kann?!
-
testmensch schrieb:
die ganze sache mit emplace_back von c++11 fällt schonmal weg. der konstruktor nimmt
Was meinst du damit? Verwendest du kein C++11? std::list mit emplace_back würde nämlich genau das machen: Einmal erzeugen, automatisch entfernen, weder kopieren noch verschieben.
Also machst du entweder deine Klasse moveable (erfordert C++11) und kannst sie trotzdem verwenden oder du nutzt boost::ptr_container oder einen container aus unique_ptr.
-
Belli schrieb:
ich würde Objekte der Klasse vector<meinTyp*> gar nicht benutzen, so dass streng genommen das oben aufgeführte Prinzip gar nicht verletzt werden kann?!
Dass du dich als Programmierer beherrschen kannst, die möglichen Fehler nicht zu begehen, das glaube ich dir. Das Problem ist jedoch, dass du nun die Möglichkeit dieser Fehler überhaupt erst geschaffen hast. Das heißt, jeder muss ungeheuer aufpassen, wenn er deinen Code benutzt, dass er nicht über die von dir fahrlässig hinterlassenen Fallstricke stolpert. Mit dem Argument, das du es im Moment für dieses eine Programm richtig machst, könntest du sonst jede Perversion der Sprache und jeden noch so verworrenen Spagetticode rechtfertigen. Hauptsache läuft?
Gerade bei einem wiederbenutzbaren Containerklassentemplate ist einmalige, vorsichtige Benutzung ja wohl überhaupt nicht gegeben.
-
ja ich verwende kein c++11, daher fällt list und emplace_back weg (das war mir schon im voraus bewusst), auch die klasse moveable zu machen fällt weg. unique_ptr ist nicht so in c++03 zu implementieren dass man es in einem vektor speichern kann oder? sonst bleibt mir nur ein vector aus shared_ptr oder ein ptr_vector aus boost übrig.
-
Belli schrieb:
In meiner Implementation ist vector abgeleitet von _Vector_val.
Wieso gibt es da einen Unterschied zu der Situation, in der ich vector_pointer von vector ableite? Es gibt also sowieso schon den abgeleiteten Container ohne virtuellen Destruktor ...OMG
Es geht nicht um den fehlenden virtuellen Destruktor, sondern darum, dass std::vector einfach nicht darauf ausgelegt ist "subclassed" zu werden.
(EDIT: Der nicht-virtuelle Destruktor ist bloss ein Wink mit dem Zaunpfahl dahingehend dass std::vector eben nicht darauf ausgelegt ist dass man davon ableitet.)D.h. du kannst von std::vector ableiten, grundsätzlich kein Problem. So lange du nicht versuchst bestehende Funktionalität in irgend einer Art und Weise zu ändern. Oder Funktionen wie push_back etc. zu überdecken, um z.B. irgendwas mitzubekommen. Weil das eben nicht geht.
(Die std::vector Funktionen sind nicht virtual, also kann man nix überschreiben, also kann man sich auf nix verlassen.)Beispielsweise können bestimmte STL Algorithmen auf std::vector spezialisiert sein. Jetzt können zwei Dinge passieren:
- Die Spezialisierung greift nicht mehr, weil z.B. ein Function-Template spezialisiert wurde (im Gegensatz zu einem Overload mit einem weiteren Function-Template).
Dann wird die "falsche" Version des Algorithmus ausgeführt. - Die Spezialisierung greift, aber bekommt eine Referenz vom statischen Typ std::vector<...>& mit. Und ruft über diese die nicht-virtuellen Funktionen auf (push_back, was auch immer). Also die von std::vector, und NICHT deine.
Versuch einfach mal es zu machen. Dann probier folgende einfache Dinge:
* Füg' ein Element mit std::back_inserter ein
* "Lösch" ein paar Elemente mit std::remove_if OHNE nachfolgendem std::vector::erase
* Lösch ein paar Elemente mit std::remove_if MIT nachfolgendem std::vector::erase
Und guck ob irgendwelcher Unsinn dabei rauskommt.Weiters ist std::vector kopierbar. D.h. man kann aus deinen abgeleiteten Vektor einen normalen std::vector kopieren. Macht auch irgendwie keinen Sinn wenn dein abgeleiteter Vektor besitzende Zeiger enthält.
=> Die STL Container sind nicht darauf aus dass man von ihnen Ableitet.
Dass die STL std::vector selbst so implementieren darf, dass std::vector von irgendeiner internen, nicht dokumentierten, nicht standardisierten und vor allem mit einem Reserved-Name versehenen Hilfsklasse erbt, spielt dabei überhaupt keine Rolle. Das sind Implementierungs-Details um die du dich nicht zu kümmern brauchst.
- Die Spezialisierung greift nicht mehr, weil z.B. ein Function-Template spezialisiert wurde (im Gegensatz zu einem Overload mit einem weiteren Function-Template).