Smartpointer übergeben
-
wow, derselbe Link von dove und mir: 2 dumme, ein Gedanke

-
sutter

-
Danke schon mal für die ausführlichen Erläuterungen und die Links, irgendwann raffe ich es auch noch. :p
In einem anderen Thema hatte ich ja schon mal Referenzen auf Objekte als Argumente übergeben und in einer Liste gespeichert. Als Tipp hatte ich erhalten, dass dies nicht so eine gute Idee ist und Zeiger die bessere Wahl wären. Und jetzt scheiden sich möglicherweise die Geister, die einen sagen am besten nur noch smartpointer verwenden, um den möglichen Problemen mit Pointern aus dem Weg zu gehen. Ich habe auch schon gelesen, Smartpointer nur zu verwenden, wenn es erforderlich ist. Dann bin ich über eine Diskussion gestolpert, ob es besser ist Smartpointer als Referenz zu übergeben oder nicht.
Aktuell möchte ich Objekte in einem anderen Objektes in einer Liste speichern. Als "Anwendung" habe ich mir jetzt mal eine Art von Textadventure herausgesucht, d.h. es gibt ein Item (das ist ein Gegenstand im Spiel) ein Item kann keine, eine oder mehrere ItemUseRules beinhalten (die Liste). Eine ItemUseRule ist ein Wrapperobjekt das ein weiteres Item (mit dem kombiniert werden kann) und ein Ergebnisitem (das dabei rauskommt) enthält.
Rufe ich ein Item->useWith(einAnderesItem) auf, wird die Liste mit den Regeln durchlaufen und geschaut, ob es eine Regel für dieses Item gibt und im Fall eines Erfolgs wird das Ergebnis zurück geliefert.
Ungefähr: Benutze "Rostiges Messer" mit "Schleifpapier" => "Scharfes Messer"
Ob das jetzt ein geiles Konzept ist oder ob das anders besser zu lösen ist, darum geht es erst mal nicht. Es geht darum etwas halbwegs sinnvolles zu erstellen und dabei auch ein paar Konzepte zu lernen. Außerdem gefallen mit Adventures

Später soll ein "Gameobjekt" eine Liste von "Locations" enthalten. Die Locations wiederum enthalten "Items", "Persons" und "Exits". Über die Exits mit "ExitRules" gelangt man in andere Locations usw. Personen können auch selbst wieder Items enthalten.
Noch mal zurück zu den Fragen: Sind Smartpointer (unique oder shared) die richtige Wahl? Und wenn, übergebe ich dann doch besser den Smartpointer by value?
Danke für die Hilfe,
temi
-
@wob & dove: Der Link ist wirklich sehr interessant und ich hoffe tatsächlich dadurch viele Fragen beantwortet zu bekommen. Heute abend lese ich weiter...
Nur eine kleine Frage vorher:
Ein Smartpointer ist ja ein Wrapper um ein anderes Objekt, bzw. Zeiger auf ein Objekt.
Übergebe ich eine Smartpointer const dann verhindere ich damit doch nur, dass der Wrapper verändert wird, nicht das Objekt auf das er zeigt, oder?
-
richtig
Ein Smartpointer ist ja ein Wrapper um ein
anderes Objekt, bzw.Zeiger auf ein Objekt.Er Unterstützt dich bei der Ressourcenverwaltung, (freigeben) .
Wichtiger Punkt bei der Ressourcenverwaltung ist halt "membership"Übergebe ich eine Smartpointer const dann verhindere ich damit doch nur, dass der Wrapper verändert wird, nicht das Objekt auf das er zeigt, oder?
richtig.
In der Konsequenz verhinderst du damit das resetten und releasen des Pointers.Const pointer wrappen (in bezug auf eigentümerschaft) macht in der Praxis aber selten sinn.
Wenn du jemanden einen const pointer zum arbeiten gibst, dann überträgst du ihm meistens auch nicht die eigentuemerschaft auf den pointer.
Deshalb langt da auch der wrapper freie pointer (const Myclass * p) als parameter.
Übergibts du die eigentuemerschaft, muss der neue besitzer wiederum in der lage sein die ressource freizugeben (aka den pointer zu deleten), was meist mit nem const pointer nicht geht ...Deshalb:
- Eigentümershaft (bzw sharing) bedingt die nicht const variante.
- const pointer brauchen den Wrapper (und das wissen ueber den Wrapper) nicht - const pointer als parameter langt.Gibt natuerlich ausnahmen, aber hoffentlich wenige ... ^^
-
temi schrieb:
Übergebe ich eine Smartpointer const dann verhindere ich damit doch nur, dass der Wrapper verändert wird, nicht das Objekt auf das er zeigt, oder?
Probiere es doch mal aus. Kannst ja mal testen, ob folgendes bei dir compiliert oder ob es einen Fehler bzgl. const gibt.
#include <memory> int main() { const auto cuptr = std::make_unique<int>(42); *cuptr = 23; }In dem auch schon irgendwo von mir verlinkten Video von Herb Sutter über Leak-Freedom wird ein
const unique_ptrz.B. für Pimpl vorgeschlagen. Da wäre es ja irgendwie kontraproduktiv, wenn man dann das ensprechende Impl-Objekt nicht mehr ändern könnte.
-
So früh am morgen und ich schreibe schon wieder Quatsch. Vergiss meinen letzten Absatz, der ergibt keinen Sinn. Dann könnte man ein Objekt ja nicht mehr moven. Der const unique_ptr ist für ein Array fixer Größe sinnvoll.
Erst nachdenken, dann schreiben. Bin noch im Trump-Modus: erst schreiben, dann nachdenken

-
This is the preferred way to express a widget-consuming function, also known as a “sink.”
// Smelly 20th-century alternative void bad_sink( widget* p ); // will destroy p;Das verstehe ich nicht, wieso wird p zerstört?
---
Nehmen wir mal an, bezogen auf meine Ausführungen weiter oben, ich hätte eine Klasse "ItemCatalog" in der alle verwendeten "Items" gespeichert und abgerufen werden können. Rufe ich die Funktion
neuesItem = einItem->useWith(einAnderesItem), dann wäre das ja eine Funktion die die Informationen von "einAnderesItem" benötigt, aber nicht verändern darf/muss und wiederum ein Item zurück liefert. Das wäre doch ein Fall von:
shared_ptr<item>& useWith(shared_ptr<item>& einItem);Andererseits kann es ja sein, dass die Kombination von zwei Items nicht möglich ist und die Rückgabe demnach ein "nullptr" ist. Also muss es wohl eher:
item* useWith(item* einItem);lauten.
Mindestens für den Rückgabewert.Habe ich das jetzt einigermaßen richtig verstanden?
In dem Fall wären meine im "ItemCatalog" gespeicherten Items wohl eher unique_ptr<Item>. Damit ist der "ItemCatalog" Owner und übernimmt die Verwaltung der Items. Wenn ich ein Item abrufe dann etwa so:
item* get(int id) // jedes Item hat eine Id, nehmen wir mal so an { return items[id]->get(); // items ist z.B. eine map<int, unique_ptr<Item>> }Danke für die Unterstützung meiner Schwerfälligkeit,
temiEdit: Statt items[id].get() => items[id]**->**get()
-
Noch als Ergänzung zum vorherigen Beitrag.
Wenn ein Item eine Id hat, dann wäre es natürlich auch möglich jedem Item, z.B. im Konstruktor einen Verweis zum "ItemCatalog" mitzugeben und alles andere über die Id zu erledigen.
//Item.h int useWith(int itemId)Die benötigten Infos können dann vom Item selbst aus dem Katalog abgerufen werden.
Da ich aber lernen möchte, will ich auch die andere Variante verstehen.
Gruß,
temi
-
temi schrieb:
This is the preferred way to express a widget-consuming function, also known as a “sink.”
// Smelly 20th-century alternative void bad_sink( widget* p ); // will destroy p;Das verstehe ich nicht, wieso wird p zerstört?
Steht doch da: nämlich im Kommentar! Von außen sieht man es der Funktion nicht an. Im Kommentar steht "will destroy p", also muss man sich darauf verlassen, dass irgendwo in der Implementierung von bad_sink ein "delete p" stehen wird.
Das stinkt, weil man es der Signatur gerade NICHT ansehen kann und nur aus dem Kommentar (sofern überhaupt vorhanden) erfährt. Genau deswegen ist ein modernes Sink (unique_ptr by value) klarer: man sieht sofort an der Signatur, dass Ownership des Objektes an die Funktion transferiert wird und man braucht keinen Kommentar mehr.
-
wob schrieb:
temi schrieb:
This is the preferred way to express a widget-consuming function, also known as a “sink.”
// Smelly 20th-century alternative void bad_sink( widget* p ); // will destroy p;Das verstehe ich nicht, wieso wird p zerstört?
Steht doch da: nämlich im Kommentar! Von außen sieht man es der Funktion nicht an. Im Kommentar steht "will destroy p", also muss man sich darauf verlassen, dass irgendwo in der Implementierung von bad_sink ein "delete p" stehen wird.
Ach so. Der Kommentar ist für den Aufrufer der Funktion gedacht, um darauf hinzuweisen, dass die Funktion den Zeiger freigeben wird. Ich hatte das anders interpretiert.