[C++] SmartPointer

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Raven280438
Establishment
Beiträge: 140
Registriert: 03.10.2010, 20:14

[C++] SmartPointer

Beitrag von Raven280438 »

Hi,

ich beschäftige mich erst seit kurzem mit Smart-Pointern (unique_pts) in C++.
Bisher hab ich immer mit new und delete gearbeitet.

Ich sitze jetzt schon 2 Tage dran, aber ich werde damit einfach nicht warm.

Daher meine Frage an die erfahrenen Spieleprogrammierer:
Lohnt es sich, dran zu bleiben? Bringen SmartPointer so viele Vorteile?
Oder kann ich beim alten new/delete bleiben (was für mich viel einfacher und logischer ist)?


Gruß
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von dot »

Zu sagen, dass es sich lohnt, wäre sogar noch eine Untertreibung. Smart Pointer sind essentieller Bestandteil von modernem C++.
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von xq »

Da ich aus der Managed Welt (.NET) komme, sind mir Smart Pointer echt lieb. Wir nutzen sie zur Zeit in einem Uni-Projekt (shared_pointer und weak_pointer) und ich hab das ganze auf "fast garbage collector" Biveau geholt. Heißt, new/delete funktioniert nur via factory die dann einen shared_pointer erstellt und nur der shared_pointer hat zugriff auf den destruktor.
So hat man eben den speicher wirklich so lange am leben wie eine referenz darauf existiert, und sobald die letzte ref darauf verloren geht, wird auch der speicher freigegeben.

Das war nötig, da wir teilweise keine konkrete zugehörigkeit zu objekten hatten (heißt, die ownership für den pointer wurde mehrmals verschoben und das zu tracken war fürn arsch)

also imho Ja, es lohnt sich dran zu bleiben.
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von Krishty »

Smart Pointer lösen ein Symptom, aber nicht das Problem. Und das ist, dass Programmteile schlecht durchdacht und übermäßig komplex sind.

Bei klaren Besitzstrukturen hat man auch klare Freigabezuständigkeiten, und wenn man auf Lokalität achtet, kann man alles blockweise wegschmeißen.

Das Aufräumen im Fehlerfall ist sowieso hinfällig weil Ausnahmen nur an zustandsfreien Stellen verwendet werden sollten (sonst sind sie fast unmöglich auf Korrektheit zu prüfen).

Ich fasse seit einem Jahr Smart Pointer nur noch in Ausnahmefällen mit der Kneifzange an und fahre eigentlich ganz gut damit.

Also: Frag dich mal, warum du überhaupt eine dynamische Allokation brauchst, und wie du die Anzahl der dynamischen Allokationen sowohl über die Laufzeit als auch über den Quelltext minimieren kannst. Und warum man überhaupt „Smartness“ braucht um zu wissen, wann das Ding freigegeben werden soll. Das wird dich ziemlich sicher zu einem besseren Programm führen als jeder Smart Pointer.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [C++] SmartPointer

Beitrag von Gene »

@MasterQ32: Seit Im C++11 Standard gibt es auch std::make_shared(...). Damit musst du nicht mal mehr new aufrufen. : http://en.cppreference.com/w/cpp/memory ... ake_shared

@Krishty: Ich stimme nur halb zu. Ich bin der Meinung das Smart Pointer sowohl das Symptom als auch das Problem lösen können. Setzt man sie wahllos ein, dann verstecken sie nur das Problem. Erkennt man aber das dahinter liegende Problem, kann man damit sehr schön und kurz memory leaks aus dem Weg gehen.

Ich denke das Smart Pointer nüztlich sein können. Am Besten kann man sie nutzten, wenn man sicher new/delete beherscht.
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von xq »

Das Problem, dass $Leute noch new aufrufen, besteht aber weiterhin. Wenn new private ist und gefriended erstellt wird, hat man das problem nicht mehr.
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Raven280438
Establishment
Beiträge: 140
Registriert: 03.10.2010, 20:14

Re: [C++] SmartPointer

Beitrag von Raven280438 »

Hi,

danke für eure Antworten.
Da werde ich mich wohl weiterhin mit SmartPointern beschäftigen müssen.

Folgendes Problem hab ich im Moment:

Ich habe eine Klasse Graphics, die als Oberklasse für alle Grafiksachen zuständig ist.
Dieser Klasse übergebe ich im Konstrutkor eine Refernz auf eine andere Klasse, damit diese damit arbeiten kann.
Mit normalen Pointern funktioniert das wunderbar.

Nur bekomm ich das mit nem unique_ptr nicht hin...

Code: Alles auswählen

//Header
class Graphics
{
public:
	Graphics(std::unique_ptr<MyClass> myclass);
private:
	std::unique_ptr<MyClass> MyClass;
[...]
};

//cpp
Graphics::Graphics(std::unique_ptr<MyClass> myclass)
{
	this->MyClass = myclass; //Das hier funktioniert nicht...
[...];
}
Wie lautet der Code richtig? Oder bin ich da mit unique_ptr falsch? Eigendlich gehört die Klasse MyClass noch einer anderen Klasse, also wäre shared_ptr hier nicht angebracht, oder?


Gruß
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von Helmut »

Smart Pointer zu benutzen, nur damit man Smart Pointer benutzt, ist Quatsch. Man kann auch mit Smart Pointern in C++ viel Quatsch machen. Deshalb würd ich dir einfach empfehlen, keine zu benutzen. Mit der Zeit wirst du dann automatisch ein Verständnis dafür bekommen, vielleicht mal selbst einen Smartpointer basteln und dann auch wissen, wann und wo man die einsetzt und welche Vor- und Nachteile sie haben.

Aber falls du drauf bestehst: ein unique_ptr drückt immer ein Besitzverhältnis aus, in deinem Code versuchst du aber einen solchen Pointer zu kopieren und unique_ptr kann man nicht kopieren (weil es eben nur einen Besitzer gibt). Du hast drei Möglichkeiten:
- Der Parameter verzichtet in der Zuweisung auf den Besitz via std::move
- Der Parameter übernimmt nicht erst den Besitz indem du ihn zu ner Referenz auf nen unique_ptr machst und den Member zu nem normalen Pointer machst.
- Du benutzt in dem Fall überhaupt keinen unique_ptr, da die Klasse garnichts am Besitzer ändern will. (unique_ptr soll auch nicht normale Pointer ersetzen, die sind weiterhin notwendig)
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von xq »

Shared Pointer wäre hier genau das was du brauchst. Wenn ich die Doku richtig gelesen habe, kannst du einen unique_ptr gar nicht kopieren sondern sondern musst ihn immer referenziert übergeben...
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: [C++] SmartPointer

Beitrag von Spiele Programmierer »

Hier liegt ganz offensichtlich ein Missverständnis vor.
An dieser Stelle besitzt die Klasse "Graphics" das "MyClass" Objekt nicht, es verwendet es bloß. Da kein Besitzverhältnis existiert bist du folglich mit einem Smart-Pointer schonmal falsch. Mit dem guten alten Zeiger wie es sie schon in C gab bist du hier immernoch besser beraten.

Der "std::unique_ptr" muss nur beim Besitzer liegen. Die (übergeordnete) Klasse die sich auch um das Aufräumen kümmert und damit für die Existenz dieses Objekts zuständig ist. Eine alternative Möglichkeit ist noch mit einem "std::shared_ptr" vorzugehen. Davon kann es mehrere geben und das Objekt wird erst dann freigegeben, wenn der letzte Besitzer(der letzte "std::shared_ptr") zerstört wird. In wirklich den aller meisten Fällen ist es jedoch zu empfehlen einen einzelnen klaren Besitzer festzulegen und dort einfach nur den "std::unique_ptr" einzusetzen. Vermutlich ist das bei dir einfach die Klasse die die "Graphics"-Instanz anlegt.

Der Unterschied zwichen "std::unique_ptr" und "std::shared_ptr" ist einzig die Anzahl der Besitzer. Gibt es kein Besitzerverhältnis braucht es auch keinen Smart Pointer.

@MasterQ32
"std::make_shared" ist als weiteren Grund wegen des Laufzeitoverheads vorzuziehen. In C++14 gibt es auch noch "std::make_unique" das im Prinzip nichts weiter macht als direkt ein "std::unique_ptr"-Objekt zu erzeugen.

Nebenbei möchte ich Anmerken dass das "Das war nötig, da wir teilweise keine konkrete zugehörigkeit zu objekten hatten" vermutlich der ursächliche Fehler ist. Da trifft dann wahrscheinlich auch zu was Krishty mit Symtombekämpfung meinte.

@Kristhy
Deine Argumentation kann ich teilweise nicht ganz nachvollziehen um ehrlich zu sein. Besitzverhältnisse werden doch gerade mit Smart-Pointern gefördert. Vorher war es Sache der Fantasie des Programmierers und der Dokumentation, was wie wen besitzt. Mit Smart-Pointern wird das explizit mit angegeben und macht so die Besitzverhältnisse erst richtig deutlich und hilft damit sogar eher noch beim Verbessern dieser.

Dynamische Allokationen sind häufig notwendig. Sie vermeiden zu wollen ist ein hübsches Ziel, kann aber nicht immer funktionieren. In wie fern möchtest du Ausnahmen an "zustandsfreien Stellen"(was auch immer du damit meinst. Ein Programm hat immer einen Zustand) vermeiden? Ausnahmen können in der Praxis überall auftreten. Schon bei "new" geht es los, aber auch sonst an allen möglichen Stellen.

Insgesamt betrachtet sehe ich das alles als Argumente den Nutzen von Smart-Pointern geringer einzuschätzen, ich sehe aber kein Argument sie nicht zu benutzen und jetzt wieder auf Raw-Pointer, Speicherfreigabe nach Lust und Laune und keine expliziten Besitzer zurückzukehren.

EDIT:
Shared Pointer wäre hier genau das was du brauchst.
Ne, eigentlich gar nicht. Besser wäre es einen richtigen Besitzer zu finden(den es wie mir scheint, wahrscheinlich sogar schon gab) und diesen mit einem stink normalen "std::unique_ptr" als solchen festzulegen. Die Besitzverhältnisse komplett auszuhebeln und einfach jeden zum Besitzer machen, halte ich für einen extrem schlechten Vorschlag.
Zuletzt geändert von Spiele Programmierer am 11.09.2014, 20:49, insgesamt 2-mal geändert.
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von xq »

Spiele Programmierer hat geschrieben:@MasterQ32
"std::make_shared" ist als weiteren Grund wegen des Laufzeitoverheads vorzuziehen. In C++14 gibt es auch noch "std::make_unique" das im Prinzip nichts weiter macht als direkt ein "std::unique_ptr"-Objekt zu erzeugen.

Nebenbei möchte ich Anmerken dass das "Das war nötig, da wir teilweise keine konkrete zugehörigkeit zu objekten hatten" vermutlich der ursächliche Fehler ist. Da trifft dann wahrscheinlich auch zu was Krishty mit Symtombekämpfung meinte.
Ja, Entwurfsfehler und ein Team, dass bisher nur Java programmiert hat. Kann man leider nur einen der Punkte wirklich beheben, aber dann ist das Programm voller Memory Leaks und Dangling References.

Code: Alles auswählen

int *getPtr() {
int i = 42;
return &i;
}
Sowas gab es leider allzu oft und da dachte ich, ich code mein Zeug einfach so, dass man damit keine Memory Leaks erzeugen kann, wenn man es nicht erzwingt...
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von Krishty »

Spiele Programmierer hat geschrieben:Besitzverhältnisse werden doch gerade mit Smart-Pointern gefördert. Vorher war es Sache der Fantasie des Programmierers und der Dokumentation, was wie wen besitzt. Mit Smart-Pointern wird das explizit mit angegeben und macht so die Besitzverhältnisse erst richtig deutlich und hilft damit sogar eher noch beim Verbessern dieser.
Die Besitzverhältnisse siehst du erst, wenn du den Smart Pointer verstanden hast, der da benutzt wird. (Niemand versteht Smart Pointer sofort, denn sonst würde es Threads wie diesen ja nicht geben.) Irgendwann mag O(verstehe alle Smart-Pointer-Klassen) kleiner als O(verstehe alle Besitzverhältnisse ohne Smart Pointers) sein, aber … nach meiner Erfahrung muss das Projekt dafür sehr groß sein.
In wie fern möchtest du Ausnahmen an "zustandsfreien Stellen"(was auch immer du damit meinst. Ein Programm hat immer einen Zustand) vermeiden?
Ich möchte sie überall außer dort vermeiden! Klar; zustandsfrei muss irgendwie eingegrenzt werden. Sagen wir, dass die Wärmeabstrahlung der CPU nicht dazuzählt :D Sagen wir – das ist etabliert – dass die Sammlung aller Variablen und ihrer Werte den Zustand ausmacht. Führe ich eine pure Funktion aus, befindet sich das Programm nach der Rückkehr (falls der Rückgabewert weggeschmissen wird) im selben Zustand wie vorher. Dort sind Ausnahmen (und damit Smart Pointers) nützlich – weil sie nichts kaputtmachen können. Der Zustand bleibt, wie er ist; egal, was die Funktion macht. Nur braucht man dann auch keine Smart Pointers mehr, weil alle Ressourcen lokal sind (und dafür automatische Speicherverwaltung ausreicht).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Raven280438
Establishment
Beiträge: 140
Registriert: 03.10.2010, 20:14

Re: [C++] SmartPointer

Beitrag von Raven280438 »

Hi,
Der "std::unique_ptr" muss nur beim Besitzer liegen. Die (übergeordnete) Klasse die sich auch um das Aufräumen kümmert und damit für die Existenz dieses Objekts zuständig ist. Eine alternative Möglichkeit ist noch mit einem "std::shared_ptr" vorzugehen. Davon kann es mehrere geben und das Objekt wird erst dann freigegeben, wenn der letzte Besitzer(der letzte "std::shared_ptr") zerstört wird. In wirklich den aller meisten Fällen ist es jedoch zu empfehlen einen einzelnen klaren Besitzer festzulegen und dort einfach nur den "std::unique_ptr" einzusetzen. Vermutlich ist das bei dir einfach die Klasse die die "Graphics"-Instanz anlegt.
Das heist, in der übergeordneten Klasse lege ich den unique_ptr normal an, und an die anderen Klassen gebe ich ihn nur als normalen Zeiger mit .get() weiter?


Gruß
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: [C++] SmartPointer

Beitrag von Spiele Programmierer »

Ja, ganz genau. Dafür einfach die guten alten Pointer verwenden.
Ich vermute ganz stark, das es auch das ist, was du hier machen willst und solltest. ;)

Theoretisch gibt es halt noch weitere Möglichkeiten...
  1. Das "Graphics"-Objekt wird danach tatsächlich alleiniger Besitzer. Das heißt, wenn die "Graphics"-Instanz stirbt, wird auch "MyClass" zuerstört. Dann müsste der "std::unique_ptr" tatsächlich in die "Graphics"-Klasse. Man verschiebt also praktisch die Verantwortung für die Zerstörung in die Grafikklasse. Wenn das dein Wunsch ist, solltest du dir mal C++11 Move anschauen, falls du dich damit noch nicht beschäftigt hast. Ein weiteres sehr mächtiges Feature mit hoher Praxisrelevanz.
  2. Du hast mehrere Besitzer und es wird eben mit "std::shared_ptr" erst gelöscht wenn alle Besitzer weg sind.
Der Unterschied liegt halt prinzipiell einfach nur darin wann gelöscht. Ganz ohne Smart-Pointer sondern mit altmodischen Pointer nimmt die Klasse einfach gar keinen Einfluss mehr auf das Löschen.
Ich möchte sie überall außer dort vermeiden!
Ups... habe ich verwechselt.

Wenn die Ausnahme jedoch nicht abgefangen wird(was ja vermutlich meistens der Fall ist...) propagiert sie doch in die übergeordneten Funktionen und findet sich damit auch wieder an einer nicht "zustandsfreien Stelle"? Wie willst du mit Ausnahme-/Fehlersituationen Verfahren, wenn du dich in einer nicht "zustandsfreien Stelle" befindest, also Ausnahmen dort "nicht nützlich sind", wie du behauptest?

Nebenbei halte ich auch für lokale Ressourcen Smart-Pointer für nützlich. Das spart viele Zeilen mit unnötigen "delete" und niemand kann versehentlich das "delete" vergessen. Das passiert ja leicht, besonders auch bei späteren Wartungsarbeiten. ;)
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [C++] SmartPointer

Beitrag von Gene »

Hi

Code: Alles auswählen

	this->MyClass = myclass; //Das hier funktioniert nicht...
Angenommen das würde gehen. Dann gebe es danach zwei Objekte die den Pointer verwalten 'this->MyClass' und 'myclass'.
Es wird versucht die Funktion 'unique_ptr::operator=(const unique_ptr&)' aufzurufen. Diese Funktion ist aber als private makiert, damit der Compiler einen Fehler wirft. Denn kopieren von unique_ptr ist verboten. Sonst gebe es plötzlich zwei Besitzer.

Was du willst, ist das der Besitzer gewechselt wird. Dies kannst du mit folgendem Code machen:

Code: Alles auswählen

	this->MyClass = std::move(myclass); // Das sollte gehen
Danach sollte this->Myclass ein gültiger Zeiger haben und myclass nicht mehr.

Was hier aussieht nach einer Kopieroperation ist eigentlich keine. std::move(myclass) Wandelt myclass vom Type 'MyClass' nach 'MyClass&&' um.
Das führt dazu das die funktion 'unique_ptr::operator=(unique_ptr&&)' aufgerufen wird.
Hier bei handelt es sich um den 'Move Assignment Operator' http://en.cppreference.com/w/cpp/language/move_operator
Benutzeravatar
Jonathan
Establishment
Beiträge: 2371
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von Jonathan »

In meinem Projekt benutze ich derzeit ausschließlich unique_ptr und habe den Eindruck, damit sehr gut zu fahren.
Essentiell dafür ist, dass man move-Semantik verstanden hat. Ohne alles gelesen zu haben, aber dieser Artikel hier ist sehr gut:
http://www.codeproject.com/Articles/397 ... -Cplusplus
Wenn man unique_ptr konsequent benutzt, weiß man immer genau, wem ein Objekt gehört. Soll eine andere Klasse es benutzen, aber nicht besitzen, bekommt sie einen normalen Pointer darauf (via unique_ptr::get()). Man muss dann halt garantieren, dass dieses Objekt kürzer lebt, als das Objekt, dem das Objekt gehört (äh ja. Der Besitzer muss also immer länger leben, als alle Benutzer. (als grobe Richtlinie).
shared_ptr sind für mich häufig ein Zeichen, dass entweder move-Semantik nicht verstanden wurde und man versucht, unique_ptr zu kopieren, anstatt zu verschieben (was nicht geht), oder das man keine Ordnung darin hat, wem jetzt ein Objekt eigentlich gehört (was auch ganz nett zu wissen ist, sollte man mal Objekte speichern und laden wollen). unique_ptr richtig eingesetzt garantieren, dass du keinen Memory-Leak hast und haben gleichzeitig keinerlei Overhead, wie es z.B. bei sämtlichen Garbage-Collectors der Fall ist.

Meiner Meinung nach, ist es unerlässlich unique_ptr verstanden zu haben. Es ist bloß ein langer Nachmittag, dafür musst du dir für den Rest deines Lebens aber auch keine Gedanken mehr über Memory-Leaks machen.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
hagbard
Beiträge: 66
Registriert: 05.08.2010, 23:54

Re: [C++] SmartPointer

Beitrag von hagbard »

Das Beispiel von Raven lässt sich doch mit einer Referenzvariable als member viel besser lösen. Ich sehe hier da keinen wirklichen Anwendungsfall für einen Smart-Pointer.
Wo ich jedoch Smart-Pointer zu schätzen gelernt habe ist bei APIs die irgendwie das Factory Pattern einsetzen. Weil spätestens wenn man an den Nutzer einer API ein dynamisch allokiertes Objekt übergeben muss geht eigentlich nichts ausser shared Pointer. Klar könnte man auch den Nutzer dazu bewegen das Objekt wieder freizugeben oder einen Puffer der Factory mit zu geben (wenn man weiss was man tut kann man immer alles machen ;) ) aber erfahrungsgemäß schiesst sich der durchschnittliche Programmierer mit der Shared Pointer Variante am unwahrscheinlichsten in den Fuss.
Move-Semantics sind schick aber spielen mit nicht-C++11 fähigen Compilern / Libraries nicht wirklich zusammen.
Raven280438
Establishment
Beiträge: 140
Registriert: 03.10.2010, 20:14

Re: [C++] SmartPointer

Beitrag von Raven280438 »

Hi,

danke für eure Antworten, das bringt mich weiter! ;)

Eine Frage hab ich noch:
Angenommen ich hab eine Resourcen-Loader Klasse, mit einer Methode LoadTexture(std::string pfad).
Sollte ich da einen unique_ptr zurückgeben (mit std::move).
Oder doch einen shared_ptr?

Ich tendiere eher zu Ersterem. Was denkt ihr?


Gruß
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von xq »

Ich würde da auf einen Shared Pointer gehen. Die Textur wird ja später nicht nur von einem Objekt verwendet, oder? Von daher werden sie die Objekte alle einen Pointer teilen müssen und die Textur gehört nicht einem spezifischen Objekt (außer du willst eine eigene Textur für jedes Objekt laden, aber davon würde ich abraten)
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von dot »

MasterQ32 hat geschrieben:Ich würde da auf einen Shared Pointer gehen. Die Textur wird ja später nicht nur von einem Objekt verwendet, oder?
Verwenden vs. besitzen ist ein wesentlicher Unterschied! ;)
Benutzeravatar
Jonathan
Establishment
Beiträge: 2371
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von Jonathan »

Das hängt davon ab, wie du deine Texturen verwaltest.
Normalerweise würde der Resourcen-Loader ja alle bereits geladenen Texturen kennen, damit er, wenn eine Textur zum zweiten mal angefordert wird, er sie nicht nochmal laden muss. Die Frage ist jetzt, zu welchem Zeitpunkt der Ressourcen-Loader die Texturen wieder vergessen soll.

Szenario 1: Er vergisst sie nie. Einmal geladen hält er bis zum Ende eine Referenz auf die Textur. In diesem Fall machen shared_ptr keinen Sinn, denn der Resourcen-Loader ist eindeutig der Besitzer. Er selber speichert einen unique_ptr und gibt raw-pointer raus. In diesem Falle musst du alle Spielobjekte die Texturen haben zerstören, bevor der Resourcen-Loader zerstört wird, was trivial ist, da man Objekte ja oft eh in der Reihenfolge freigibt, wie man sie erzeugt hat. Shared_ptr sind hier also überflüssig, machen das Programm langsamer und bringen überhaupt keinen Vorteil. So mach ich es bisher.

Szenario 2: Du willst Texturen, die nicht mehr benutzt werden aus dem Speicher schmeißen. Der Resourcenmanager speichert shared_ptr und gibt solche auch raus. Wenn das Level komplett geladen ist, wird der Ressourcenmanager aufgeräumt, alle shared_ptr werden gelöscht. Wenn jetzt das letzte Objekt, dass die Textur benutzt zerstört wird, wird auch die Textur freigegeben. Wird ein neues Objekt erzeugt, dass diese Textur benutzt, muss sie neu geladen werden, du musst also sehen, wie das den Spielfluss stört. Außerdem würde bei dieser Lösung die Textur ein zweites mal geladen, sollte zur Spielzeit ein Objekt mit der selben Textur geladen werden. Um das zu verhindern, könntest du nur Texturen im Manager löschen, die mindestens noch 2 Benutzer haben (einmal im Manager und einmal in irgendeinem Spielobjekt) - das müsstest du aber natürlich in jedem Frame prüfen.
Letztendlich ist halt auch die Frage, wie viele Texturen im Level nachgeladen werden. Also ob du eine Menge Streaming betreibst. Wenn während dem Level nicht viel dazu kommt und alle Texturen am Anfang geladen werden müssen, brauchst du sie auch nicht vorzeitig freigeben - denn es muss ja am Anfang mit allen Texturen gleichzeitig auch flüssig laufen.

Was man bedenken muss: Texturen braucht man nur im Resourcen-Loader zu löschen, wenn sie dadurch tatsächlich komplett freigegeben werden. die paar unique_ptr oder shared_ptr Objekte sind vom Speicherverbrauch irrelevant, einzig interessant ist, ob die Textur auf die sie zeigen gelöscht wird oder nicht. Wenn du shared_ptr benutzen willst, musst du dir also zwingend Gedanken darüber machen, wie und wann du Texturen freigeben willst, ansonsten hast du dadurch keinerlei Vorteile. Und was du natürlich auch gut machen kannst, ist für das neue Level den Resourcen-Loader einmal zu clearen, um überflüssige Texturen rauszuschmeißen. Oder, wenn du Texturen über mehrere Level behalten willst, merkst du dir einfach welche beim Laden des neuen Levels benutzt wurden und schmeißt die anderen raus. Das geht aber mit unique_ptr genauso gut.


Insgesamt halte ich shared_ptr in den allermeisten Fällen für eine schlechte Wahl. Weil es oft zeigt, dass man nicht verstanden hat, wie sein eigenes Programm funktioniert und nichtmal weiß wem welches Objekt gehört. Bei shared_ptr muss man nicht nachdenken und kann hoffen "das so schon alles gutgehen wird". Aber es gibt Fälle, wo das nicht der Fall ist, und wenn man dann Speicherveraltung nicht verstanden hat, hat man ein Problem. Also sollte man es vielleicht einfach von Anfang an richtig machen.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
antisteo
Establishment
Beiträge: 854
Registriert: 15.10.2010, 09:26
Wohnort: Dresdem

Re: [C++] SmartPointer

Beitrag von antisteo »

Krishty hat geschrieben:Smart Pointer lösen ein Symptom, aber nicht das Problem. Und das ist, dass Programmteile schlecht durchdacht und übermäßig komplex sind.

Bei klaren Besitzstrukturen hat man auch klare Freigabezuständigkeiten, und wenn man auf Lokalität achtet, kann man alles blockweise wegschmeißen.
Was leider exakt dann nicht mehr aufgeht, wenn nebenläufige Threads mit dem Speicher arbeiten und zur Designzeit nicht klar ist, welcher von beiden eher fertig sein wird.
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] SmartPointer

Beitrag von Krishty »

Freigeben muss immer, wer es auch allokiert hat. Selber Allokator. Selbes Modul. Selber Thread.

Mag mit Speicher noch gut funktionieren, das durchzumischen. Sobald andere Ressourcen dazwischenkommen (Fenster?), kracht es.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
antisteo
Establishment
Beiträge: 854
Registriert: 15.10.2010, 09:26
Wohnort: Dresdem

Re: [C++] SmartPointer

Beitrag von antisteo »

Krishty hat geschrieben:Freigeben muss immer, wer es auch allokiert hat. Selber Allokator. Selbes Modul. Selber Thread.

Mag mit Speicher noch gut funktionieren, das durchzumischen.
Selbst mit nur Speicher ist es kompliziert genug.

Wir haben in unserer Experimentier-Datenbank eine ausgeführte Anfrage, die in einem Hintergrund-Thread gleichzeitig optimiert wird. Sobald die Ausführung fertig ist, wird die Anfrage schon befreit, der Optimierer läuft aber noch auf denselben Daten.
2 Möglichkeiten: Entweder zum Befreien synchronisieren oder nicht befreien und stattdessen Garbage Collector benutzen.
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
Antworten