SmartPointer - von bereits initilisierten Variablen



  • cl90, wenn Du "Klasse" sagst, meinst du manchmal offensichtlich "Objekt". Verwechsel diese zwei Dinge nicht.

    cl90 schrieb:

    shared_ptr<_Console> pcons(new _Console());
    

    _Console gehört zu den reservierten Namen, die du nicht verwenden darfst. Vermeide einfach alles mit zwei aufeinanderfolgenden Unterstrichen sowie Namen, die mit Unterstrich anfangen und mit einem Großbuchstaben weitergehen.
    Außerdem solltest Du das durch

    auto pcons = make_shared<Console>();
    

    ersetzen. Das ist effizienter so.

    cl90 schrieb:

    Aber was mache ich mit bereits initialiserten Variablen?
    bzw macht sowas Sinn:

    void _Console::find_command(shared_ptr<string> str)
    {
    	//doSth
    }
    

    Oder kann/sollte man hier vlt. einfach eine referenz verwenden?
    Es wäre auch call by value möglich, es ging mir nur darum nicht 100 Zeichen zu schicken, sondern nur einen Pointer.

    Was Du hier machen solltest, hängt davon ab, was die Funktion machen soll. Ich kann mir Situationen vorstellen, wo das so, wie Du es geschrieben hast, sinnvoll ist. Ich kann mir aber auch vorstellen, wo das hier besser wäre:

    void Console::find_command(string const& str);
    

    cl90 schrieb:

    [...] Trotzdem bekomme ich beim Schließen des Programms, einen Heap Error.

    Dann verwendest du shared_ptr wahrscheinlich falsch.

    shared_ptr<int> foo = make_shared<int>(1729);
    shared_ptr<int> bar (foo.get()); // FALSCH!
    

    Hier wissen foo und bar nichts voneinander. Beide glauben, alleiniger Besitzer des int-Objekts zu sein. Es gibt zwei Referenzzähler. Das ist einer zuviel. Der erste shared_ptr von ihnen, der zerstört wird, zieht dem anderen das int-Objekt unter'm Arsch weg.

    shared_ptr<int> foo = make_shared<int>(1729);
    shared_ptr<int> bar = foo; // RICHTIG!
    

    Hier gibt es nur einen einzigen Referenzzähler und bei der Kopie wird ordnungsgemäß dieser eine Zähler erhöht.



  • cl90 schrieb:

    BOOL testDlg::OnInitDialog()
    {
    
    	m_pTXTConsole	= shared_ptr<CString>(&m_TXTConsole);
    	m_pCData		= shared_ptr<_CData>(new _CData(m_pTXTConsole));
    	m_pConsole		= shared_ptr<_Console>(new _Console(m_pCData));
    
    	return TRUE;
    }
    

    Du kannst nicht einfach nen shared_ptr auf eine MEMBERVARIABLE machen und dann erwarten dass es funktioniert.
    Oder würdest du delete &m_TXTConsole; schreiben und dich dann wundern wenn es knallt? Nein? Wieso versuchst du das selbe dann mit nem Smart-Pointer?



  • Skym0sh0 schrieb:

    void print(Foo const& f){} // weil es wird nur gelesen, daher reicht const&
    
    void swap(Foo & a, Foo & b){} // die änderungen sollen ja nach außen sichtbar sein
    // daher &
    
    void bar(Foo tmpCopy){} // hier willst du explizit eine Kopie haben
    // du willst vllt diese Kopie verändern und lesen und und, aber du willst keine Änderungen nach außen lassen
    

    Danke!
    Und auch an alle anderen, ich habe mir hier vieles abgeguckt und mommentan gibt es nur eine Situation in der ich noch nicht um den raw pointer gekommen bin. ansonsten verwende ich shared, oder wie von Sky die ref/const ref.

    Worum ich bisher nicht gekommen bin:

    m_pCData->Attach_Dialog(this);
    

    Es geht um einen Observeranschluss. Alles andere hat bisher nicht funktioniert.
    m_pCData ist ein shared_ptr<_CData>



  • Oha!

    shared_ptr ist nicht dazu da, nicht mehr über Besitzverhältnisse nachzudenken zu müssen. shared_ptr ist dazu dar, sich das explizite delete zu sparen, nachdem man zum Schluss gekommen ist, dass ein geteilter Besitz (shared ownership) das Richtige ist.

    cl90 schrieb:

    mommentan gibt es nur eine Situation in der ich noch nicht um den raw pointer gekommen bin. ansonsten verwende ich shared, oder wie von Sky die ref/const ref.

    raw pointer sind nicht per se schlecht! Nutze einfach das, was die Besitzverhältnisse korrekt ausdrückt! Du scheinst shared_ptr einfach so zu nutzen, ohne das mit den Besitzverhältnissen verstanden zu haben.



  • Ja ich denke es gibt immer noch Dinge gibt die ich nicht oder nicht richtig weiß.

    Aber ich hab die besitzverhältnisse zu shared_ptrn schon verstanden.

    bla()
    {
    	int* i = new int(10);		// wird gelöscht sobald } erreicht wird
    
    	shared_ptr<int> j(new int(10));	// wird auch gelöscht sobald } erreicht wird
    
    	foo(j);			// sofern foo j irgendwo speichert wird das neue int nicht bei } gelöscht. 
    }
    

    So habe ich die shared_ptr verstanden. Die letzte instanz des pointers löscht das object. alle anderen nur den pointer den sie haben.



  • cl90 schrieb:

    bla()
    {
    	int* i = new int(10);		// wird gelöscht sobald } erreicht wird
    

    Nein, wird nicht gelöscht.



  • Ah ok. also muss ich für diesen Fall explizit delete aufrufen.
    Danke, hab ich in meinem Beispiel nicht durchdacht.



  • MichelRT schrieb:

    Gemischt mit RAW-Pointern und Smartpointern zu arbeiten ist nicht ganz trivial. Da gibt es einige Fallstricke.

    Und ob es trivial ist. Immer wenn ein Zeiger sein Objekt besitzt, nimmst du Smart-Pointer. Ansonsten nimmst du rohe Zeiger.

    MichelRT schrieb:

    Wenn Du das wirklich tun möchtest würde ich Dir das nur für eine Übergangszeit empfehlen bis das komplette Projekt auf Smartpointer umgestellt ist.

    Nein, rohe Zeiger sind durchaus berechtigt, wenn du sie für Verweise benutzt.

    MichelRT schrieb:

    Schau Dir boost::enable_shared_from_this und die Möglichkeiten eigene deleter zu erstellen mal an.
    Damit kann man ganz nette Sachen machen. z.B. kannst Du wenn du irgendwo einen shared_ptr eines Objektes hast feststellen ob an anderer Stelle der Raw-Pointer mit delete zerstört wurde 😉

    Klingt nach grober Frickelei. In einem vernünftigen Design ist sowas nicht nötig (siehe meinen ersten Satz).

    shared_ptr ist überbewertet. In den allermeisten Fällen braucht man ihn nicht. Nehmt bitte unique_ptr wenn ihr Smart-Pointer braucht, und shared_ptr nur in exotischen Fällen, wo tatsächlich geteilter Besitz notwendig ist (d.h. wo nicht im Voraus ein eindeutiger Besitzer festgestellt werden kann). shared_ptr aus Faulheit zu nehmen wird sich schnell rächen.

    cl90 schrieb:

    Ah ok. also muss ich für diesen Fall explizit delete aufrufen.
    Danke, hab ich in meinem Beispiel nicht durchdacht.

    Das ist aber so ziemlich das Grundlegendste von Speicherverwaltung! Bevor du Smart-Pointer verwendest, solltest du dir dieses Thema definitiv nochmals genau anschauen.

    <💡>



  • Mich würde mal interessieren wie viele der Leute die vor übermässigem Einsatz von shared_ptr warnen damit bereits selbst auf die Fresse gefallen sind.

    Ich verwende shared_ptr seit vielen Jahren sehr freizügig, und hatte damit bisher noch kaum ernste Probleme.
    Bisher genau einen Fall wo ich mir im Nachhinein gedacht habe dass es ziemlich sicher besser gewesen wäre unique_ptr zu verwenden.
    (Was in dem Projekt aber sowieso nur sehr umständlich möglich gewesen wäre, da kein Move-Support.)

    Daher gehe ich davon aus diese Sache - wie so vieles - einfach gerne nachgeplappert wird. Ohne dass die Leute, die nie müde werden es überall zu posten, wirklich selbst Erfahrung damit hätten.



  • glühbirne schrieb:

    MichelRT schrieb:

    Gemischt mit RAW-Pointern und Smartpointern zu arbeiten ist nicht ganz trivial. Da gibt es einige Fallstricke.

    Und ob es trivial ist. Immer wenn ein Zeiger sein Objekt besitzt, nimmst du Smart-Pointer. Ansonsten nimmst du rohe Zeiger.

    Mit "gemischt" meine ich Raw-Zeiger und shared_ptr auf das gleiche Objekt. 😃

    Wenn man etwas neu macht ist es natürlich grober Unfug und das Vorgehen ist da klar.



  • In der Tat habe ich noch nicht viele schlechte Erfahrungen gemacht mit dem shared_ptr. Aber eine schon, da hats mir aufgrund von zyklischen Abhängigkeiten einiges zerschossen und ich durfte debuggen und suchen und machen und tun bis ich nur diese Abhängigkeit entdeckt habe. Und dann musste ich nochmal gut nachdenken, wer wo was jetzt Sinn macht, wer den Besitz inne hält. Den bzw. die anderen Pointer konnten danach auf weak_ptr umgestellt werden.

    Aber es ist im Prinzip das alte leidliche Problem:
    "mh, shared_ptr, klappt, macht alles automatisch, super muss ich nix machen."
    Aber dass da die Zuständigkeiten und sowas geändert werden könnten, oder von anfang an nicht klar sind, das merken kaum welche.



  • hustbaer schrieb:

    Ich verwende shared_ptr seit vielen Jahren sehr freizügig, und hatte damit bisher noch kaum ernste Probleme.

    Die Frage ist, was du als Probleme betrachtest. Bis sich die Performance von Reference-Counting/Threadsicherheit bemerkbar macht, braucht es natürlich schon etwas. Das meinte ich nicht primär.

    Wesentlicher finde ich allerdings das, was krümelkacker angesprochen hat: Man denkt weniger über Besitzverhältnisse nach oder betrachtet shared_ptr als GC-Ersatz.

    Was ich z.B. schon gesehen habe, ist dass Relationen gänzlich mit shared_ptr modelliert werden, obwohl gar kein geteilter Besitz notwendig ist. Z.B. Objekt B muss A referenzieren. Natürlicherweise würde A genug lange leben und man kann ohne Probleme einen Verweis A* in B speichern. Aber es wird shared_ptr<A> genommen, weil das ja praktisch ist. Blöderweise kann man jetzt keine automatischen Objekte mehr referenzieren. Ah, kann man ja doch, indem man einen Custom-Deleter schreibt, der nichts tut (jedoch verlieren wir dabei die Garantie der starken Referenz). Meine Erfahrung war, dass solche Designs schlussendlich komplexer waren als wenn man die naheliegendste Lösung genommen hätte.

    Anderes Beispiel ist das massiv überbenutzte vector<shared_ptr<X>>, obwohl man fast immer vector<unique_ptr<X>> oder ptr_vector<X> möchte.

    hustbaer schrieb:

    Daher gehe ich davon aus diese Sache - wie so vieles - einfach gerne nachgeplappert wird.

    Ich persönlich habe das Gefühl, dass in dem Zusammenhang viel eher wie wahnsinnig gesagt wird, man müsse unter allen Umständen Smart-Pointer benutzen, ohne zu differenzieren. Leute glauben, RAII verstanden zu haben, und benutzen nur noch shared_ptr. Dabei ist die ursprüngliche Absicht von shared_ptr auf einen spezifischen Anwendungsfall ausgelegt.

    Hängt wahrscheinlich auch damit zusammen, dass sogar Boost selbst shared_ptr als Allheilmittel propagiert (sie benutzen ihn sogar für Pimpl). Vielleicht auch, weil scoped_ptr extrem eingeschränkt ist -- hier stehts um unique_ptr ja mittlerweile besser.

    Ich will shared_ptr nicht schlechtreden, ich brauche ihn selbst ab und zu. Gerade in Verbindung mit weak_ptr kann er wahnsinnig praktisch sein. Aber für den Thread hier, wo nicht mal new/delete verstanden wurde, ist shared_ptr ziemlich sicher nicht das Richtige. Man sieht auch schön die Auswüchse ("ich konnte leider noch nicht alle rohen Zeiger entfernen"). Benötigt wird eigentlich nur RAII, und das geht auch mit automatischen Objekten (oder unique_ptr wenn nötig).

    <💡>



  • MichelRT schrieb:

    Mit "gemischt" meine ich Raw-Zeiger und shared_ptr auf das gleiche Objekt. 😃

    Bei shared_ptr ist es eher unüblich, rohe Zeiger auf das Objekt zu haben, von Funktionsparametern mal abgesehen. Wenn man eine schwache Referenz benötigt, nimmt man weak_ptr.

    Mit unique_ptr hingegen ist es wirklich einfach: unique_ptr<X> hält das Objekt am Leben, und diverse X* sind einfach passive Verweise darauf. Ist nichts anderes als wenn man direkt X und X* hätte.

    <💡>



  • Nachtrag: Rohe Zeiger auf Objekte innerhalb shared_ptr sind gar nicht vorgesehen; nicht ohne Grund hat shared_ptr keine get()-Methode.

    "Mischen" ist also nicht so gut.

    <💡>



  • glühbirne schrieb:

    MichelRT schrieb:

    Mit "gemischt" meine ich Raw-Zeiger und shared_ptr auf das gleiche Objekt. 😃

    Bei shared_ptr ist es eher unüblich, rohe Zeiger auf das Objekt zu haben, von Funktionsparametern mal abgesehen. Wenn man eine schwache Referenz benötigt, nimmt man weak_ptr.

    Klar ist es unüblich, aber wenn man so wie der TO ein größeres Projekt umbaut, kann es für eine gewisse Zeit dazu kommen, dass man beides hat.
    Bei neuem Code wäre es hirnverbrannt sich auf so etwas einzulassen 🤡



  • glühbirne schrieb:

    nicht ohne Grund hat shared_ptr keine get()-Methode.

    Klar hat es die

    http://www.cplusplus.com/reference/memory/shared_ptr/get/



  • Ah richtig, sorry hatte ich falsch in Erinnerung (war release(), und ging um Zeiger wieder rausnehmen). Danke für die Korrektur.

    <💡>


Anmelden zum Antworten