factory + smart pointer
-
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.
-
ich bin nicht der meinung, dass der client selber entscheiden können sollte ob er einen schlauen oder einen dummen pointer will. wenn die factory smartpointer zurückliefert, dann sichert man damit ein bestimmtes verhalten zu.
dumme pointer könnten z.b. einfach gelöscht werden, obwohl es an anderen stellen noch x referenzen auf das dahinter liegende objekt gibt. der client weiß einfach nicht, wie lange das objekt gültig bleibt. oder ob, wann und wie er es löschen darf, soll oder muss. wenn ich meine bisherigen erfahrungen so betrachte, ist das eine typische fehlerursache.
mit einem smartpointer ist es klar: der letzte macht das licht aus und der provider entscheidet, wie der lichtschalter aussieht. das ist genau das, was ich meistens will.
dass smartpointer evtl. mehr performance kosten, spielt an dieser stelle eigentlich gar keine rolle. die objekte werden ja nicht in einer tour erzeugt und wieder zerstört.
@tachyon: wo genau liegt denn die problemlösung bei deinem workaround? ich verstehe nicht, worauf du hinaus willst. dein code hat das gleiche problem hat wie meiner (base_factory::create() und derived_factory::create() mit nicht kovarianten rückgabetypen).
dass die verwendung von boost als nachteil gewertet wird, ist in diesem fall echt lustig. ich habe mich nach jahrelanger weigerung dazu entschieden, boost dieses mal eine chance zu geben, statt das rad immer wieder neu zu erfinden. und schon beim ersten problem kommt der vorschlag, es doch lieber selbst zu programmieren

-
fabrikarbeiter schrieb:
dass die verwendung von boost als nachteil gewertet wird, ist in diesem fall echt lustig.
Find ich eher traurig. Boost ist zu einem nicht kleinen Teil von Leuten die im C++ Standardkomitee sitzen beeinflusst wurden. Ich behaupte mal das boost zu den besten C++ Bibliothekssammlungen überhaupt zählt.
Wenn ich mit Strings arbeite verwende ich auch nicht char* sondern std::string, und schreibe keine eigene Umsetzung.
Ich verstehe hier eh nicht warum sich die Leute hier wegen Performance und Overhead aufregen. Wozu gibt es Profiler, wozu gibt es Refactoring, und wo ist bitte die Sicherheit bei dem Ganzen.
Vorzeitige Optimierung ist die Wurzel allen Übels. Warum nicht erst einmal sicheren Code schreiben und dann später die Teile anpassen die tatsächlich ein Flaschenhals in der Anwendung sind. Ich habe inzwischen die Erfahrung in meinen Programmen gemacht, das obwohl ich intensiv mit Smartpointern und ähnlichen Technologien arbeite meine Programme schnell sind. Okay, das liegt natürlich auch an den Anwendungsbereichen. Wenn ich innerhalb kürzester Zeit Abertausende Objekte anlegen muss gibt es besseres - wobei dort dann auch die Frage ist ob nicht replacement new verwendet wird, oder ganz andere Speicherverwaltungsregeln gelten sollten (und spätestens bei Zweiten können nackte Zeiger auch wieder problematisch sein).
cu André
-
asc schrieb:
...
Da bin ich absolut deiner Meinung.Meist liegt ein Performanceeinbruch eh nicht dort, wo man am Anfang erst vermuten würde und es ist schlussendlich viel ineffizienter, wenn du durch vorranige Suche und kopfzerbrechen etwas machst, was schlussendlich eh nicht mehr draufankommt, sondern ein völlig anderer Teil Probleme macht.
-
Wer meint, Smartpointer machen "auch nur ein delete", dem sei dieser Artikel ans Herz gelegt:
http://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/sp_techniques.htmlMit Shared Pointern kann man theoretisch z.B. full-featured Connectionpooling implementieren. Da ist es aber wenig hilfreich, wenn man dem Aufrufer die Wahl der Verwaltung/Zerstörung überlässt.
-
asc schrieb:
Ich verstehe hier eh nicht warum sich die Leute hier wegen Performance und Overhead aufregen. Wozu gibt es Profiler, wozu gibt es Refactoring, und wo ist bitte die Sicherheit bei dem Ganzen.
Wenn du damit micht meinst, ich habe nie das Wort Performance erwähnt oder sonstiges in der Richtung.
Mir gehts nur darum, dass der arme Benutzer der Lib sich nicht extra zusätzlich boost draufmachen muss. Bei einer kleinen Lib fänd ichs ziemlich lästig, aber es hängt auch von der Zielgruppe ab.
Der Entwickler kann das wohl am besten entscheiden. Wenn es aber keine Lib wird und es um ein eigenes Projekt geht, dann spricht rein gar nichts gegen boost
