Smart Pointer
-
Jupp, habe Visual Studio Ultimate.
-
Ja sehr gut. Ich hab hier "nur" die 2012 Version, weiss also nicht so ganz ob das bei älteren Versionen anders aussieht.
Auf jedenfall gibt es oben die DropDown-Menus Architektur und Analysieren.
Bei Architektur kannst du unter "Abhängigkeitsdiagramm generieren" dir einmal die Includeabhängigkeiten und die, naja, kA wie man das nennen soll, Klassenabhängigkeiten zeichnen lassen. Sehr hilfreich um Code zu verstehen oder zu starke Kopplungen zu suchen.
Und unter Analysieren kannst du mit "Leitungsanalyse starten" dein Programm profilen.
Einfach mal nen Standardfall laufen lassen und dann wird der dir den Bericht anzeigen. Einmal als zeitliches Diagramm visualisiert, wo dann auch zu sehen ist, wann dein Programm schluckt. Und dann zeigt er die Funktionen an, wo die meiste bei draufgegangen ist, da kannst du dich schön durchklicken. Sehr geil wie ich finde. Spiel eifnach mal da rum.Macht mehr Spass als Programmieren

-
Werde ich mir mal ansehen. Zu Testzwecken habe ich mir mal jeweils im Contructor und Destructor der Framework Basis-Klasse einen statischen Zähler eingebaut um zu sehen wie oft Objekte erstellt und wieder zerstört werden. Die Überraschung: Die Destructoren werden ca. 1/3 mal so oft aufgerufen wie die Constructoren. Kann mir das gerade nicht erklären.
Naja, habe jetzt also mal angefangen, dort wo die Owner-Zugehörigkeit klar ist die rohen Zeiger durch shared_ptr zu ersetzen. Die Frage ist wie weit ich das treiben soll, denn SmartPointer soll man ja nur dort verwenden wo auch sinnvoll.
Wenn ich also ein Property:static SharedPtr<Property> Width = ...habe, sollte ich dann auch bei Funktionen, die dieses Property übergeben bekommen einen SharedPtr nehmen oder doch einen rohen Zeiger, denn die Funktion "besitzt" das Property ja nicht:
void Control::SetValue(Property* p, Base* value); // oder void Control::SetValue(SharedPtr<Property> p, Base* value);
-
Rein von der richtigen Architektur und den Zuständigkeiten her, ist ein shared_ptr in sehr vielen Fällen unangebracht.
Aber aus technischer Sicht hilft er fast überall, und da wo er nicht hilft, kommt ein weak_ptr hin.
Aber das ist halt die rein technische Sicht. Da passieren keine Memory-Leaks, aber es kann sein, dass gewisse Entitäten länger leben als gewollt.
Ich selbst würde erstmal so vorgehen.
- Alle Pointer durch shared_ptr ersetzen.
- dann alle zyklischen Referenzen durch weak_ptr aufbrechen.
- ab jetzt sollte kein Memory-Leak mehr auftreten
- dann würde ich alle Zuständigkeiten abklären, aufbröseln und den shared_ptr durch unique_ptr ersetzen (Achtung, dadurch muss im Normalfall das kopieren des Objekts manuell geregelt werden, muss beim shared_ptr aber auch schon vond er Semantik her)Und wenn du dann noch merkst, dass Smart-Ptr unpassend sind, kannst du immer noch back2the roots gehen und rohe Pointer benutzen. (Wird aber nicht nötig sein)
-
Reicht's fürs Multithreading nicht, den gesamten Baum zu locken, solange er geschrieben wird? Ich würd's mir nicht so kompliziert machen. Gerade wenn die Lesevorgänge fürs UI sind, muss das ja nicht so schnell sein.
-
Hi, ich habe jetzt mal Testweise in meinem Programm die rohen Zeiger angefangen durch SmartPointer zu ersetzen. Leider macht das ein paar Probleme.
Wie umgehe ich z.B. folgendes Problem, das mit normalen Zeigern funktioniert:std::shared_ptr<Base> Test(std::shared_ptr<Base> test) { std::shared_ptr<Derived> ptr(test); ptr->DerivedSpecificFunction(); return ptr; // Geht nicht, da kein passenender Konstruktor für die Konvertierung von Derived in Base vorhanden. }Und was mache ich wenn verschiedene Zeiger Typen aufeinander stoßen, ich also z.B. einen std::vector<std::shared_ptr<Object>> habe der nun aber auch einen Zeiger auf ein Objekt aufnehmen soll, das von einem std::unique_ptr<Object> verwaltet wird?
-
Sorry, im Beispiel oben fehlt noch ein dynamic_pointer_cast, aber so gehts auch nicht:
std::shared_ptr<Base> Test() { std::shared_ptr<Derived> ptr(new Derived()); ptr->DerivedSpecificFunction(); return ptr; // Geht nicht, da kein passenender Konstruktor für die Konvertierung von Derived in Base vorhanden. }
-
Du sollst doch nicht alle rohen Zeiger durch Smart-Pointer ersetzen! Nur die besitzenden, sprich die, die in irgendeiner Weise bspw. den Speicherplatz des Pointees verwalten!
Edit: Ich sehe, das zweite Beispiel ist schon passender.
-
Merkwürdig, das sollte automatisch gehen. Compiler (+Version)?
-
Hier macht shared_ptr gar keinen Sinn. Abgesehen davon, dass sich dein Problem auch mit shared_ptr lösen lässt, so macht man das richtig:
std::unique_ptr<Base> Test() { Derived *d = new Derived; unique_ptr<Base> b(d); d->DerivedSpecificFunction(); return b; }
-
Benutze Visual Studio 2012. Das würde ich ja gerne tun, aber wenn der Owner meiner Objekte eindeutig wäre bräuchte ich gar keine SmartPointer, dann würde ich die Objekte einfach im Destructor deleten und fertig. Leider ist die Praxis eine andere. Stellt euch vor ich habe Resourcen die am Programmstart geladen und ganz am Schluss wieder freigegeben werden. Diese würde man z.B. mit einem unique_ptr verwalten, da hier der Owner klar ist. Oder besser ganz ohne SmartPointer. Eine solche Resource könnte ein Style sein. Dieser wird nun dem Style-Property-Zeiger eines Controls zugewiesen. Aber der User kann darüber hinaus auch selber einen Style dynamisch erzeugen und diesem Style-Property zuweisen. Woher soll das Style-Property nun aber wissen ob dieser Style womöglich eine Resource ist und später automatisch gelöscht wird oder ob sich das Control selber darum kümmern muss?
Mal ein wenig Pseudocode ohne SmartPointer, dann wirds vielleicht versändlicher:
Style* GetDefaultStyleFromResourceManager(Type type) { ... return style; } class Control { public: Control(); void SetStyle(Style* style); private: Style* style; }; Control::Control() { style = GetDefaultStyleFromResourceManager(...); } void Control::SetStyle(Style* style) { this->style = style; } int main() { Style* myCustomStyle = new Style(); myCustomStyle->Background(255, 255, 0, 0); Control* myControl = new Control(); myControl->SetStyle(myCustomStyle); return 0 }Wie Sorge ich nun dafür, dass der myCustomStyle gelöscht wird?
Und bitte empfehlt mir jetzt nicht diesen am Ende der main-Funktion zu löschen, das ist jetzt nur ein vereinfachtes Beispiel.
-
Oder noch ein schönes Beispiel:
Control::GetTransform() { TransformGroup* group = new TransformGroup(); const Vector& offset = GetOffset(); group->Add(new TranslateTransform(offset.X(), offset.Y())); const Vector& scale = GetScale(); group->Add(new ScaleTransform(scale.X(), scale.Y())); return group; }void Control::ApplyTransforms() { Object* obj = this; TransformGroup* group = new TransformGroup(); while (obj) { group->Add(obj->GetTransform()); obj = obj->Parent(); } ... }Jetzt werdet ihr euch wahrscheinlich fragen warum ich diese Transformationen überhaupt dynamisch erzeuge. Ganz einfach. Das liegt an meinem Propertysystem und daran, dass natürlich auch Transformationen Ressourcen sein können. Man kann z.B. für jeden Control-Type eine Default-Transformation im ResourcenManager abrufen, aber auch Benutzerdefinierte-Transformationen zuweisen.
-
Aber genau dafür sind SmartPointer doch da o.O Damit musst du dir über das deleten keine größeren Gedanken machen.
-
Wie Sorge ich nun dafür, dass der myCustomStyle gelöscht wird?
Indem du unique_ptr nutzt. Einfach:
private: std::unique_ptr<Style> style; };Und wenn der Style eine gemeinsame Ressource ist, dann eben shared_ptr.