factory + smart pointer
-
warum nicht einfach einen normalen zeiger zurueck geben?
der anwender kann dann ja eh immer smartpointer verwenden.denn smartpointer - vorallem shared_ptr sind nicht immer sinnvoll.
-
hallo
Shade Of Mine schrieb:
warum nicht einfach einen normalen zeiger zurueck geben?
weil ein shared_ptr für meine situation sehr gut passen und das interface vereinfachen würde.
Tachyon schrieb:
Irgendwie glaube ich nicht, dass das so kompiliert.
da glaubst du falsch. das nennt sich "covariant return types" und ist teil des c++-standards.
-
fabrikarbeiter schrieb:
hallo
Shade Of Mine schrieb:
warum nicht einfach einen normalen zeiger zurueck geben?
weil ein shared_ptr für meine situation sehr gut passen und das interface vereinfachen würde.
Tachyon schrieb:
Irgendwie glaube ich nicht, dass das so kompiliert.
da glaubst du falsch. das nennt sich "covariant return types" und ist teil des c++-standards.
Jo, hast recht. Der Compiler, mit dem ich mich hier rumquälen muss ist nur zu alt und schrottig. Der kann das noch nicht.
Dann vielleicht so:
class base { public: }; class derived : public base { public: }; class base_factory { protected: virtual base* create_obj() = 0; public: boost::shared_ptr<base> create() { return boost::shared_ptr<base>(create_obj()); } }; class derived_factory : public base_factory { protected: virtual derived* create_obj() { return new derived(); } public : boost::shared_ptr<derived> create() { return boost::shared_ptr<derived>(create_obj()); } };
-
fabrikarbeiter schrieb:
hallo
Shade Of Mine schrieb:
warum nicht einfach einen normalen zeiger zurueck geben?
weil ein shared_ptr für meine situation sehr gut passen und das interface vereinfachen würde.
Und ich dachte der Thread geht darueber dass shared_ptr eben nicht passt weil es damit keine covariant return types gibt.
wie man sich irren kann...
-
Shade Of Mine schrieb:
warum nicht einfach einen normalen zeiger zurueck geben?
Weil bei einem Zeiger nicht immer sicher gestellt ist, wie er verwaltet wird. Wenn ich mit shared_ptr arbeite kann ich auch die Factory so umstellen das sie intern mit einem Cache arbeitet, ohne die Schnittstelle zu ändern.
Ja, man kann es mit Smartpointern auch übertreiben (wobei ich hier lieber übertreibe wenn ich dafür ein stabiles Programm habe - optimieren kann man immer noch), aber sie haben den entschiedenen Vorteil das die Speicherverwaltung geklärt ist.
@fabrikarbeiter
boost::shared_ptr<base> ist ein anderer Typ as boost::shared_ptr<derived>.
Der Sinn einer solchen Fabrik ist aber das der Typ nicht entscheidend ist (Polymorphie) - sprich eigentlich wäre die Rückgabe von boost::shared_ptr<base> richtig.
-
fabrikarbeiter schrieb:
Tachyon schrieb:
Irgendwie glaube ich nicht, dass das so kompiliert.
da glaubst du falsch. das nennt sich "covariant return types" und ist teil des c++-standards.
Nein, ist es nicht. Nicht so wie du es anwendest.
base* und derived* sind hierbei "covariant return types"
shared_ptr<base> und shared_ptr<derived> sind es aber nicht! Diese beiden Typen haben nichts miteinander gemeinsam.
-
asc schrieb:
fabrikarbeiter schrieb:
Tachyon schrieb:
Irgendwie glaube ich nicht, dass das so kompiliert.
da glaubst du falsch. das nennt sich "covariant return types" und ist teil des c++-standards.
Nein, ist es nicht. Nicht so wie du es anwendest.
base* und derived* sind hierbei "covariant return types"
shared_ptr<base> und shared_ptr<derived> sind es aber nicht! Diese beiden Typen haben nichts miteinander gemeinsam.
Jo, aber mit dem Workaround, den ich oben gepostet habe, kann man das evtl. umgehen. Ich kanns hier leider nicht testen, weil ich a) kein Boost installiert habe und b) mein Compiler nicht mit kovarianten Rückgabewerten klar kommt.
-
asc schrieb:
Weil bei einem Zeiger nicht immer sicher gestellt ist, wie er verwaltet wird. Wenn ich mit shared_ptr arbeite kann ich auch die Factory so umstellen das sie intern mit einem Cache arbeitet, ohne die Schnittstelle zu ändern.
Das wuerde mich aber mal interessieren. Wie sollte sowas aussehen? Mehrmals die selbe instanz liefern? Das waere aber mal sehr boese.
Man kann auch nicht definieren wie der Pointer zerstoert werden soll, denn dann muesste man das interface aendern.
Es geht hier rein um ein automatisches delete und das kann der user genauso setzen. oft ist ein scoped_ptr sinnvoller als ein shared_ptr...
shared_ptr liefern hier eben den grund fuer das problem. Da base und dervied shared_ptr keine verwandten typen sind. welchen nachteil habe ich, wenn ich einen normalen zeiger zurueck liefere?
ich habe den vorteil der covariant return types und die flexibilitaet jeden smart pointer zu verwenden den der user verwenden will.
das sind 2 vorteile: welchen nachteil habe ich? der user kann sich gegen die verwendung von smart pointern entscheiden. das ist alles.
PS:
der workaround wuerde natuerlich funktionieren, aber es ist einfach unpraktisch x<base> mit x<derived> zu mischen...
-
Shade Of Mine schrieb:
PS:
der workaround wuerde natuerlich funktionieren, aber es ist einfach unpraktisch x<base> mit x<derived> zu mischen...Tut man ja nicht. Man wird nur gezwungen, Smartpointer zu benutzen. Ansonsten ist es kein Unterschied dazu, die Pointer direkt zurückzugeben und sie dann in einen Smartpointer zu packen.
-
Shade Of Mine schrieb:
Das wuerde mich aber mal interessieren. Wie sollte sowas aussehen? Mehrmals die selbe instanz liefern? Das waere aber mal sehr boese.
Es kommt darauf an wie die Objekte gestaltet sind und um was es sich für Daten es sich handelt. Bei unveränderlichen Stammdaten kann das durchaus Sinn machen.
Oder ein anderes Beispiel:
Ich habe einen Terminkalender und viele Ansichten auf die gleichen Termine. Eine Änderung in einer der Ansichten muss auch alle anderen betreffen. Warum sollte ich hier verhindern das jede Ansicht mit der gleichen Instanz arbeitet?Shade Of Mine schrieb:
Man kann auch nicht definieren wie der Pointer zerstoert werden soll, denn dann muesste man das interface aendern.
So? Was kann das folgende den alles bedeuten?
object* create();Es könnte sich um ein klassisches "new object" handeln. Es könnte sich aber auch um einen Speicherbereich handeln der mit malloc etc generiert wurde (und wo vielleicht der replace-new operator verwendet wird). Es könnte sich um einen Wert handeln der je nachdem ob er bereits eingelesen wurde oder nicht aus einem Chache kommt usw. (Alles abhängig vom Anwendungsfall, und alles ohne die Schnittstelle zu ändern).
Es geht hier rein um ein automatisches delete und das kann der user genauso setzen. oft ist ein scoped_ptr sinnvoller als ein shared_ptr...Wenn das zurückgegebene Objekt tatsächlich nur lokal verwendet, und nur eine Instanz davon gehalten wird, ist das Sinnvoll.
das sind 2 vorteile: welchen nachteil habe ich? der user kann sich gegen die verwendung von smart pointern entscheiden. das ist alles.Nachteil:
Bei Zeigern ist nicht garantiert wie sie hergeleitet werden. Ein delete auf einen solchen Zeiger ist nicht unbedingt beabsichtigt. Bei einen shared_ptr weiß ich das die Verwaltung diesem Typ überlassen wird.Ja es hat ebenso Nachteile, aber ich sehe ebenso Vorteile. Programmierung ist nunmal eine Disziplin der vielen Wege die allesamt korrekt sein können.
cu André
-
Shade Of Mine schrieb:
Man kann auch nicht definieren wie der Pointer zerstoert werden soll, denn dann muesste man das interface aendern.
Bei einem normalen Zeiger nicht, bei einem shared_ptr kannst du festlegen, wie das gehaltene Objekt zerstört werden soll (falls es überhaupt zerstört werden soll, ein shared_ptr kann theoretisch auch auf statische Daten zeigen). Da hat man ein bisschen mehr Flexibilität.
Wie würdest du das Problem der flexiblen Freigabe lösen? Eine Methode, die das Objekt selbst zerstört (delete this; / free(this); / ...) oder eine freie Funktion (gut, ist jetzt nicht so der große Unterschied)? Oder ganz anders?
Gruß
Don06
-
Tachyon schrieb:
Shade Of Mine schrieb:
PS:
der workaround wuerde natuerlich funktionieren, aber es ist einfach unpraktisch x<base> mit x<derived> zu mischen...Tut man ja nicht. Man wird nur gezwungen, Smartpointer zu benutzen. Ansonsten ist es kein Unterschied dazu, die Pointer direkt zurückzugeben und sie dann in einen Smartpointer zu packen.
ich werde gezwungen shared_ptr<derived> zu verwenden auch wenn ich shared_ptr<base> haben will. das ist sehr wohl ein unterschied.
asc schrieb:
Bei Zeigern ist nicht garantiert wie sie hergeleitet werden. Ein delete auf einen solchen Zeiger ist nicht unbedingt beabsichtigt. Bei einen shared_ptr weiß ich das die Verwaltung diesem Typ überlassen wird.
ein shared_ptr macht auch nur ein delete.
smart pointer haben keine magie dabei...es geht darum, dass das delete automatisch gemacht wird, aber warum muss das die factory bestimmen? wenn es der anwender bestimmen kann, hat er viel mehr freiheiten und vorallem kann man dann eben auch die covariant return types verwenden.
ich sehe keinen vorteil darin shared_ptr aus einer factory methode zu liefern. denn das schoene an zeigern ist, dass ich sie jederzeit in einen smart pointer packen kann - und shared_ptr ist der 2. teuerste smart pointer den es gibt. wenn man ihn braucht sind die kosten egal, aber wozu ueberall einsetzen?
in den meisten faellen ist scoped_ptr einfach besser. vorallem ist das ganze hier ein super beispiel warum smart pointer nicht immer die beste wahl sind. man kann natuerlich sagen: ok, dann verzichte ich auch covriant return types - aber ist das nicht eher eine entscheidung die der client code treffen sollte und nicht der provider code?
denn es bringt keine abstraktion oder sonstige vorteile fuer die factory wenn sie direkt einen smart pointer liefert. es ist aber manchmal fuer den client unpraktisch. und smartpointer kann man so oder so verwenden.
-
Shade Of Mine schrieb:
ein shared_ptr macht auch nur ein delete.
smart pointer haben keine magie dabei...Nö, im Konstruktor eines shred_ptr legt man fest, ob z.B. delete, free, oder irgendeine andere Funktion aufgerufen werden soll.
http://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/shared_ptr.htm#Members
Custom deallocators allow a factory function returning a shared_ptr to insulate the user from its memory allocation strategy. Since the deallocator is not part of the type, changing the allocation strategy does not break source or binary compatibility, and does not require a client recompilation. For example, a "no-op" deallocator is useful when returning a shared_ptr to a statically allocated object, and other variations allow a shared_ptr to be used as a wrapper for another smart pointer, easing interoperability.
The support for custom deallocators does not impose significant overhead. Other shared_ptr features still require a deallocator to be kept.
-
Wäre es trotzdem nicht besser nen rohen Zeiger zurückzugeben und den Clienten entscheiden zu lassen, wo er ihn reinpacken will.
Intern kann man selbst schön mit smart Pointern arbeiten, aber nach außen sollte man einen "reinen" Typen zurückgeben, denn nicht jeder hat boost installiert.So zwingst du ja den Benutzer deiner Library auch die installation von boost. In einigen Fällen ist das zwar gut, aber bei kleinen Librarys würde ich sowas versuchen zu vermeiden.
-
asc schrieb:
Nein, ist es nicht. Nicht so wie du es anwendest.
doch, ist es. tachyons einwand war auf die nackten pointer bezogen, nicht auf die shared_ptr.
Shade Of Mine schrieb:
fabrikarbeiter schrieb:
hallo
Shade Of Mine schrieb:
warum nicht einfach einen normalen zeiger zurueck geben?
weil ein shared_ptr für meine situation sehr gut passen und das interface vereinfachen würde.
Und ich dachte der Thread geht darueber dass shared_ptr eben nicht passt weil es damit keine covariant return types gibt.
ähm ja, genau darum geht es. ein shared_ptr würde vom prinzip her für meine situation sehr gut passen und das interface vereinfachen - wenn das problem mit der fehlenden covarianz nicht wäre.
asc schrieb:
Der Sinn einer solchen Fabrik ist aber das der Typ nicht entscheidend ist (Polymorphie) - sprich eigentlich wäre die Rückgabe von boost::shared_ptr<base> richtig.
das ist mir bewusst. ich brauche an dieser stelle aber die spezialisierung. base gibt die schnittstelle vor, die nach außen hin relevant ist.
derived implementiert aber plattformspezifische details in form von zusätzlichen funktionen, die sich in base nicht sinnvoll unterbringen lassen. deshalb brauche ich intern ein derived, außerhalb aber nur ein base.Shade Of Mine schrieb:
in den meisten faellen ist scoped_ptr einfach besser.
aber der löst auch nicht rein zufällig mein covarianz-problem, oder?
-
KasF schrieb:
Wäre es trotzdem nicht besser nen rohen Zeiger zurückzugeben und den Clienten entscheiden zu lassen, wo er ihn reinpacken will.
Gut, dann wiederhole ich meine Ernst gemeinte Frage:
Don06 schrieb:
Wie würdest du das Problem der flexiblen Freigabe lösen? Eine Methode, die das Objekt selbst zerstört (delete this; / free(this); / ...) oder eine freie Funktion (gut, ist jetzt nicht so der große Unterschied)? Oder ganz anders?
-
Shade Of Mine schrieb:
ein shared_ptr macht auch nur ein delete.
smart pointer haben keine magie dabei...Nicht "auch nur ein delete", sondern eine Zusicherung: Es wird ein delete durchgeführt. Den bei den normalen Zeigern ist es nicht sicher das ein delete beabsichtigt ist!
Shade Of Mine schrieb:
ich sehe keinen vorteil darin shared_ptr aus einer factory methode zu liefern. denn das schoene an zeigern ist, dass ich sie jederzeit in einen smart pointer packen kann - und shared_ptr ist der 2. teuerste smart pointer den es gibt. wenn man ihn braucht sind die kosten egal, aber wozu ueberall einsetzen?
Um sicherzustellen das die richtige Freigabestrategie gewählt wird, unabhängig davon wie es hinter der Schnittstelle aussieht. Du hast recht das der shared_ptr teuer ist, aber ich Optimiere nicht im voraus, wenn ein Profiler mir sicher sagen kann, das es hier ein Problem in meinen Programm gibt, kann ich immer noch nach alternativen suchen!
Shade Of Mine schrieb:
in den meisten faellen ist scoped_ptr einfach besser.
Er schließt die Verwendung in Listen etc. aus. Ich ziehe den scoped_ptr für Daten vor, wo ich wirklich garantieren kann das sie Scopebezogen gelten.
Shade Of Mine schrieb:
vorallem ist das ganze hier ein super beispiel warum smart pointer nicht immer die beste wahl sind. man kann natuerlich sagen: ok, dann verzichte ich auch covriant return types - aber ist das nicht eher eine entscheidung die der client code treffen sollte und nicht der provider code?
Dann würde ich die Factory aber eindeutig steuern (z.B. über Policyklassen), damit GARANTIERT ist was dort zurückgegeben wird.
Shade Of Mine schrieb:
denn es bringt keine abstraktion oder sonstige vorteile fuer die factory wenn sie direkt einen smart pointer liefert. es ist aber manchmal fuer den client unpraktisch. und smartpointer kann man so oder so verwenden.
Es gibt einen ganz gewaltigen Vorteil den ich aber auch schon erwähnt hatte: Eine Garantie der Freigabestrategie.
-
Don06 schrieb:
KasF schrieb:
Wäre es trotzdem nicht besser nen rohen Zeiger zurückzugeben und den Clienten entscheiden zu lassen, wo er ihn reinpacken will.
Gut, dann wiederhole ich meine Ernst gemeinte Frage:
Don06 schrieb:
Wie würdest du das Problem der flexiblen Freigabe lösen? Eine Methode, die das Objekt selbst zerstört (delete this; / free(this); / ...) oder eine freie Funktion (gut, ist jetzt nicht so der große Unterschied)? Oder ganz anders?
Ich würde es bei meiner Library anders lösen, nämlich einen eigenen smart_ptr bereitstellen
Somit würde ich den User aufzwingen den smart_ptr meiner Lib zu benutzen um so die benutzung von boost zu vermeiden.Es ist ja ehe nicht das gelbe vom Ei nen rohen Zeiger so zurückzugeben und das weiß vermutlich auch jeder anständige C++ Programmierer, denn niemand weiß woher der Zeiger kommt und wie dafür Speicher alloziert wurde und eigene Funktion der Lib dann bereitzustellen die das Löschen übernehmen, finde ich auch ziemlich eklig.
Also fabrikarbeiter, schreib die deinen eigenen smart_ptr. Das Verhalten zu erziehlen einen smart_ptr<Derivied> in smart_ptr<Base> konvertieren zu lassen ist auch ganz einfach

-
Wer sagt denn, das man boost braucht? shared_ptr gibts im TR1.
-
HansiHinterseher schrieb:
Wer sagt denn, das man boost braucht? shared_ptr gibts im TR1.
Stimmt.
Aber leider hat momentan nicht jeder einen Compiler der TR1 bereitstellt und solange müssen die Leute auf boost::tr1 zurückgreifen.