Kann ich placement new verwenden, um ein Objekt in einem shared_ptr zurückzusetzen?

8

Sagen wir, ich habe eine Klasse.

%Vor%

Dann tue ich:

%Vor%

Später, nachdem ich mit meinem Objekt fertig bin und ich sicher bin, dass dort keine anderen Benutzer für das Objekt sind.

Ist Folgendes sicher:

%Vor%

Würde ich das Objekt ohne zusätzliche Zuweisungen zurücksetzen?

    
Nathan Doromal 03.04.2013, 20:49
quelle

4 Antworten

2

Ja, normalerweise ist es sicher. (Nicken Sie zu Maxim Yegorushkins Beobachtung über einen Fallfall)

Beachten Sie die Tippfehler-Meldung unter

Boost definiert die Dereferenz- und -> -Operatoren als

%Vor%

Wenn diese detail Bits aufgelöst sind, haben Sie dies

%Vor%

Sie haben es also direkt mit dem spitzen Objekt zu tun. Es gibt keine Proxies oder andere Konstrukte, die das, was Sie versuchen, beeinträchtigen könnten.

Da es sich um die angegebenen Daten handelt, lautet Ihr Code:

%Vor%

Kann einen Tippfehler haben. Aber wenn du es beabsichtigst:

%Vor%

Es wird aufgelöst nach

%Vor%

Dies ist legal, und Sie haben Recht, dass es die zusätzliche Zuweisung vermeiden würde, die normalerweise bei einer Zuweisung anfällt.

    
Drew Dormann 03.04.2013, 20:59
quelle
6

Es gibt ein paar Möglichkeiten, dies zu tun. Sie können Placement neu verwenden, und dies ist aus zwei Gründen garantiert sicher:

  1. Sie haben bereits den Speicher für das Objekt zugewiesen, damit Sie wissen, dass es korrekt skaliert und ausgerichtet ist.

  2. shared_ptr ist nicht invasiv; seine alleinige Verantwortung besteht darin, Referenzen zu zählen und bei Bedarf den Deleter aufzurufen.

Bedenken Sie jedoch, was passieren kann, wenn die Rekonstruktion des Objekts fehlschlägt, d. h. eine Ausnahme auslöst:

%Vor%

Dann haben Sie ein Problem: Der Deleter kann auf einem nicht konstruierten Objekt aufgerufen werden, was höchstwahrscheinlich zu undefiniertem Verhalten führt. Ich sage "fast", weil der Deleter ein No-Op sein könnte, in diesem Fall wäre alles gut.

Sicherer, ich denke, würde einen neuen Wert in das vorhandene Objekt verschieben:

%Vor%

Oder fügen Sie eine reset() -Memberfunktion zu BigData hinzu:

%Vor%

Dann ist es explizit, was Ihre wahre Absicht ist, und Sie müssen nicht so besorgt über Objektlebenszeiten sein.

    
Jon Purdy 03.04.2013 21:05
quelle
2

Es ist sicher, wenn BigData Konstruktor und Destruktor keine Ausnahmen auslöst und bigDataPtr nicht zwischen Threads geteilt wird und keine Zeiger oder Referenzen auf dynamisch zugewiesene Mitglieder von BigData (falls vorhanden) existieren.

Wenn der Destruktor eine Ausnahme auslöst, kann es sein, dass Sie ein teilweise zerstörtes Objekt erhalten (das Werfen von Destruktoren wird im Allgemeinen nicht empfohlen und Standardcontainer erfordern, dass Destruktoren von Elementen nicht geworfen werden).

Wenn der Konstruktor Sie wirft, kann es passieren, dass Sie das Objekt zerstören, aber kein neues erstellen.

Wenn bigDataPtr zwischen Threads geteilt wird, die auch zu einer Race-Bedingung führen können, es sei denn, eine Sperrdisziplin wird verwendet.

Wenn Code an anderer Stelle Verweise oder Zeiger auf dynamisch zugewiesene Mitglieder von BigData annimmt, wenn er ein neues BigData erstellt, können seine dynamisch zugewiesenen Mitglieder anderen Adressen zugewiesen werden, so dass bestehende Zeiger und Verweise auf die Mitglieder ungültig werden / p>

Wenn Sie sich mit der zweifelhaften Dereferenzierung in new (&*bigDataPtr) BigData; Anweisung beschäftigen, verwenden Sie stattdessen einen einfachen Zeiger:

%Vor%     
Maxim Egorushkin 03.04.2013 20:59
quelle
1

Erstens, wenn der Konstruktor eine Klasse wirft und die Klasse nicht einfach zerstörbar ist, dann haben Sie ein Problem, da die shared_ptr sie löschen möchte, was UB provozieren würde.

Sie müssen sich damit befassen, indem Sie entweder einen Notch-Konstruktor verwenden oder eine Ausnahme abfangen und verhindern, dass der Smart-Pointer das Objekt löscht. Da shared_ptr keine release() -Funktion hat, ist das leichter gesagt als getan. Sie könnten terminate() aufrufen, wenn alles andere fehlschlägt, aber das macht Sie bei Ihren Benutzern nicht beliebt.

Wenn es keine anderen Verweise auf das Objekt gibt, funktioniert es, vorausgesetzt, die Klasse hat keine const oder nicht-statische Referenzdatenelemente (einschließlich Membern von Membern). Der Grund ist 3,8 / 7:

  

Wenn nach der Lebensdauer eines Objekts und vor dem Speichern   was das Objekt belegt, wird wiederverwendet oder freigegeben, ein neues Objekt ist   an dem Speicherort erstellt, den das ursprüngliche Objekt belegt hat, a   Zeiger, der auf das ursprüngliche Objekt zeigt ... kann verwendet werden   manipuliere das neue Objekt, wenn ... der Typ des ursprünglichen Objekts ist   nicht const-quali fi ziert, und, falls eine Klassenart vorhanden ist, keine   nicht statisches Datenelement, dessen Typ const-quali fi ziert ist oder eine Referenz   Geben Sie ...

ein

Beachten Sie, dass shared_ptr nur einen solchen Zeiger enthält, mit dem das neue Objekt manipuliert werden kann. Das ist UB, wenn eine der Bedingungen in 3.8 / 7 kaputt ist. Der einzige, der möglicherweise kaputt ist, ist dieser, Sie haben den Rest mit dem, was Sie über Ihren Code gesagt haben, abgedeckt. Insbesondere ist es erforderlich, dass Sie das ursprüngliche Objekt als eine Instanz von BigData , nicht eine von BigData abgeleitete Klasse erstellt haben, da das neue Objekt den gleichen am meisten abgeleiteten Typ wie haben muss der alte.

Es gibt normalerweise robustere Möglichkeiten, ein Objekt zurückzusetzen. Implementieren Sie beispielsweise operator= (Zuweisungsoperator kopieren oder verschieben), und schreiben Sie dann *bigDataPtr = BigData() . Natürlich ist das vielleicht nicht ganz so schnell.

    
Steve Jessop 03.04.2013 22:23
quelle

Tags und Links