wahl des richtigen smartpointer
-
hustbaer schrieb:
nwp3 schrieb:
Die Lib erfordert sehr wohl eine vituelle clone-Funktion.
Tut sie nicht, du guckst nicht richtig.
Ok, ich habe nicht bis zu ende gelesen. Ich mag es trotzdem nicht. Man muss im gesamten Code aufpassen, dass man immer den dynamischen Typ übergibt. Nicht wirklich ein Vorteil gegenüber einer vergessbaren clone-Funktion, wo man nur in den Klassendeklarationen nach einem fehlenden clone suchen muss.
Man warte auf C++2x, dann gibts virtuelle Copy-Konstruktoren und dann funktioniert das out of the box.(Ich hasse es englisch und deutsch zu mischen)
-
nwp3 schrieb:
Man muss im gesamten Code aufpassen, dass man immer den dynamischen Typ übergibt.
Auch nur dann, wenn man zeitweise mit rohen Zeigern arbeitet, was bei Smartpointern i.d.R. vermeidbar ist. Typischerweise stammen die aus irgendwelchen Factories oder eben einem einfachen make_clone_ptr.
-
Bzgl Kopieren/Clonen hatte ich das mal ungefähr so gelöst:
#define AUTO_RETURN(...) ->decltype(__VA_ARGS__) {return __VA_ARGS__;} namespace clone_details { struct fallback_on_copy {}; struct prefer_clone : fallback_on_copy {}; template<class T> auto doit(T const* ptr, fallback_on_copy) AUTO_RETURN( new T(*ptr) ) template<class T> auto doit(T const* ptr, prefer_clone) AUTO_RETURN( ptr->clone() ) } // clone_details template<class T> struct default_clone_destroy { auto clone(T const* ptr) AUTO_RETURN(clone_details::doit(ptr,prefer_clone())) void destroy(T* ptr) { delete ptr; } };(so runtergetippt und nicht getestet)
...so dass man in den meisten Fällen einfach
default_clone_destroyzum Klonen und Löschen benutzen kann
-
Mir faellt noch eine Variante ein, grundlegend saehe das so aus:
#include <type_traits> #include <utility> template< typename T > class clone_ptr_polymorph { void* mDerivedPtr; T* mBasicPtr; std::pair<T*, void*> (* mCloner)(void*); template< typename Derived > static std::pair<T*, void*> clone(void* ptr) { auto obj = new Derived{ *reinterpret_cast<Derived*>(ptr) }; return { obj, obj }; } public: template< typename Derived, typename=typename std::enable_if<std::is_base_of<T, Derived>::value>::type > clone_ptr_polymorph( Derived* ptr ) : mDerivedPtr{ptr}, mBasicPtr{ptr}, mCloner{ &clone<Derived> } {} clone_ptr_polymorph( clone_ptr_polymorph const& p ) : mCloner{p.mCloner} { auto pair = mCloner(p.mDerivedPtr); mBasicPtr = pair.first; mDerivedPtr = pair.second; } ~clone_ptr_polymorph() { delete mBasicPtr; } clone_ptr_polymorph( clone_ptr_polymorph&& ) = default; clone_ptr_polymorph& operator=( clone_ptr_polymorph&& ) = default; }; template<typename Base, typename Der, typename... Args> clone_ptr_polymorph<Base> make_clone_ptr( Args&&... args ) { return { new Der(std::forward<Args>(args)...) }; } #include <iostream> struct Base { virtual ~Base() { std::cout << "Base-Destructor\n"; } Base() { std::cout << "Base-Def-Ctor\n"; } Base(Base const&) { std::cout << "Base-Copy-Ctor\n"; } Base(Base&&) { std::cout << "Base-Move-Ctor\n"; } }; struct Derived : Base { ~Derived() { std::cout << "Derived-Destructor\n"; } Derived() { std::cout << "Derived-Def-Ctor\n"; } Derived(Derived const&) { std::cout << "Derived-Copy-Ctor\n"; } Derived(Derived&&) { std::cout << "Derived-Move-Ctor\n"; } }; int main() { auto const& p = make_clone_ptr<Base, Derived>(); auto second = p; }http://ideone.com/MQ2xTv
(Bin gerade nicht konzentriert, da in der Schule, aber so sollte wenigstens hustbaer nicht einen halben Fetzen darin sehen)Edit: Ich glaube, der Ideone Output ist merkwuerdig... habe ich da boese rumgepfuscht? wieso taucht da
Base-Def-Ctor
Derived-Copy-Ctor
auf?
-
@hustbaer: Kann gut sein, was würdest du konkret verbessern?
@nwp3: Man muss den dynamischen Typen nur bei der Initialisierung angeben. In vielen Fällen kennt man ihn dann, weil man das Objekt gerade konstruiert. Und ansonsten nimmt man eben die clone-Strategie.
-
Ach nein, der (die Ausgabe) ist richtig. Ich habe den copy-ctor ja selbst definiert. :schaem:
-
hustbaer schrieb:
@volkard
Was würdest du mit einem kopierenden Zeiger anstellen der das nicht macht? Also der beim Kopieren ein Objekt der Basisklasse erstellt (slicing).
In so einem Fall würde ich gleich den gewünschten Typ direkt verwenden. Oder ggf.boost::optional<T>, wenn ich lazy initialization bzw. allgemein einen "empty" Zustand brauche.Oder meinst du so ein Zeiger grundsätzlich ist gut, aber er sollte nicht "einfach so" funktionieren?
Ich meinte, daß es evtl gar nicht gut ist, den Benutzer nicht zu zwingen, zu diesem Zwecke clone() selber anzubieten und in die Klassen zu schreiben, hübsch bei den großen 3/5/6.
-
Der richtige smart pointer hat einen Default-Konstruktor, der automatisch eine Instanz des Programmes das man haben möchte erzeugt. Alles andere ist doch nur Augenwischerei.
-
smarter typ schrieb:
Der richtige smart pointer hat einen Default-Konstruktor, der automatisch eine Instanz des Programmes das man haben möchte erzeugt. Alles andere ist doch nur Augenwischerei.
Aber solch ein Smart-Pointer wäre nur Penisverlängerung... Wer braucht sowas?!
-
Skym0sh0 schrieb:
smarter typ schrieb:
Der richtige smart pointer hat einen Default-Konstruktor, der automatisch eine Instanz des Programmes das man haben möchte erzeugt. Alles andere ist doch nur Augenwischerei.
Aber solch ein Smart-Pointer wäre nur Penisverlängerung... Wer braucht sowas?!
Faule Programmierer mit Freundin.
-
Seit wann haben Programmierer/Informatiker Freundinnen? oO
Und faul sind wir doch alle, per Definition. Und ungeduldig und überheblich...
-
Skym0sh0 schrieb:
Seit wann haben Programmierer/Informatiker Freundinnen? oO
Und faul sind wir doch alle, per Definition. Und ungeduldig und überheblich...
ist doch einfach...
this->girlfriend = &nadine; nadine.owner = this;sooo, bitteschön!
-
Ja, aber die meisten Programmierer sind Nullen, daher fuehrt das zu Segfaults.
-
Komisch: in der API-Referenz steht, dass Objekte der Klasse Girldfriend bei allen Operationen nicht deterministische Seiteneffekte haben. Unter anderem wird da vor dem vierten Newton'schen Axiom* gewarnt...
*: Für jede männliche Aktion gibt es eine weibliche Überreaktion.
-
Mit einem Foto von meinem billigen Notebookdisplay könnte ich dieses Mahnmal nun als "Spiegel der Programmiererseele" zu höchstpreisen verkaufen!
-
noch smarterer typ schrieb:
Skym0sh0 schrieb:
Seit wann haben Programmierer/Informatiker Freundinnen? oO
Und faul sind wir doch alle, per Definition. Und ungeduldig und überheblich...
ist doch einfach...
this->girlfriend = &nadine; nadine.owner = this;sooo, bitteschön!
Da meckert mein Kompiler:
src/main.C:95:9: error: ‘class Programmierer’ has no member named ‘girlfriend’
-
Aurorer schrieb:
@hustbaer: Kann gut sein, was würdest du konkret verbessern?
* Es wäre schön wenn man mit einer Allokation auskommt (=Objekt und "Owner" werden zusammen allokiert).
* DerPtrIndirection(PtrOwnerBase<U>* sourceBase, MoveTag)Konstruktor ist höchst fragwürdig. Der sollte auch das "Ausnullen" vonsourceBaseübernehmen - das ausserhalb zu machen ist fehleranfällig.
* Man kann nicht explizit casten (down-/cross-casts).
* Explizites Casten wiederrum kann man nicht einfach nachrüsten, da diePtrIndirectionKlasse dabei zu einem Problem wird. (Man hätte dann sehr schnell viele ineinander verschachteltePtrIndirectionInstanzen).
* Die ganzePtrIndirectionKlasse ist sowieso höchst unschön, das lässt sich sicher eleganter regeln.
* Dasvirtual PtrOwner* clone() const { assert(pointer); return new PtrOwner(cloner(pointer), cloner, deleter); }ist nicht Exception-safe.
Vermutlich noch ein paar mehr Kleinigkeiten, hab nur kurz drübergesehen.
Aber viel weiter oben angesetzt stellt sich die Frage ob die high-level Designentscheidungen überhaupt optimal sind. volkard ist da ja z.B. anderer Meinung. Und ich bin nicht sicher ob er nicht vielleicht Recht hat

-
Bekomme ich noch Feedback für meine Variante?

-
Arcoth schrieb:
Bekomme ich noch Feedback für meine Variante?

Völliger Quatsch. Sogar falsch, man kann sehr leicht double deletes produzieren.
-
attention vampir schrieb:
Völliger Quatsch.
Wäre fein, wenn du mir den Grund nennst.
Sogar falsch, man kann sehr leicht double deletes produzieren.
Natürlich habe ich Move-Semantik falsch implementiert. (Edit: So gesehen habe ich sie natürlich nicht implementiert, worin auch der Fehler liegt)
Die konkrete Umsetzung interessiert aber (vorerst) nicht so sehr, es geht ja schließlich erstmal um die Idee. Daher würde ich gerne wissen, was an der "Völliger Quatsch" ist.