Wenn ich ein Objekt deklariere, das in einen gemeinsamen Zeiger eingeschlossen ist:
std::shared_ptr<myClass> myClassObject(new myClass());
dann wollte ich es als Argument an eine Methode übergeben:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
Erhöht das oben Gesagte einfach die Referenzanzahl von shared_pt und alles ist cool? Oder hinterlässt es einen baumelnden Zeiger?
Solltest du das noch tun?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
Ich denke, dass der zweite Weg effizienter sein kann, da nur 1 Adresse kopiert werden muss (im Gegensatz zum gesamten Smart Pointer), aber der erste Weg scheint besser lesbar zu sein und ich erwarte nicht, die Leistungsgrenzen zu überschreiten. Ich möchte nur sicherstellen, dass nichts Gefährliches daran ist.
Danke dir.
c++
c++11
shared-ptr
c++-faq
Steve H.
quelle
quelle
const std::shared_ptr<myClass>& arg1
DoSomething
wirklich das Eigentum teilen? Es sieht so aus, als ob es stattdessen nur eine Referenz nehmen sollte ...void DoSomething(myClass& arg1)
.shared_ptr<>
Insbesondere müssen Sie den Wert übergeben, um das Eigentum tatsächlich zu teilen.std::make_shared
ist es nicht nur effizienter, sondern auch sicherer als derstd::shared_ptr
Konstruktor.Antworten:
Klar, das kann ich dir helfen. Ich gehe davon aus, dass Sie ein gewisses Verständnis der Besitzersemantik in C ++ haben. Ist das wahr?
Gut.
Ok, ich kann mir nur zwei Gründe vorstellen, um ein
shared_ptr
Argument anzunehmen:shared_ptr
s funktioniert .Für welches interessieren Sie sich?
Beispiele für solche Funktionen sind
std::static_pointer_cast
benutzerdefinierte Komparatoren oder Prädikate. Wenn Sie beispielsweise alle eindeutigen shared_ptr aus einem Vektor suchen müssen, benötigen Sie ein solches Prädikat.Genau.
Ja. Und wenn sich der Zeiger dadurch nicht ändert, möchten Sie die const-Referenz übergeben. Sie müssen nicht kopieren, da Sie das Eigentum nicht teilen müssen. Das ist das andere Szenario.
Der, an dem Sie das Eigentum teilen? OK. Wie teilen Sie das Eigentum mit
shared_ptr
?Dann muss die Funktion eine Kopie von a
shared_ptr
erstellen, richtig?Nein, das ist eine Pessimisierung. Wenn es als Referenz übergeben wird, hat die Funktion keine andere Wahl, als die Kopie manuell zu erstellen. Wenn es als Wert übergeben wird, wählt der Compiler die beste Wahl zwischen einer Kopie und einer Verschiebung und führt sie automatisch aus. Übergeben Sie also den Wert.
Die Funktion kann das
shared_ptr
Argument einfach in seinen Speicher verschieben. Das Verschieben von ashared_ptr
ist billig, da es keine Referenzanzahl ändert.In diesem Fall
shared_ptr
ist für die Funktion völlig irrelevant. Wenn Sie den Pointee manipulieren möchten, nehmen Sie einen Pointee und lassen Sie die Anrufer auswählen, welche Besitzersemantik sie möchten.Es gelten die üblichen Regeln. Intelligente Zeiger ändern nichts.
Richtig.
Ah, ein interessanter Randfall. Ich erwarte nicht, dass das oft passiert. In diesem Fall können Sie entweder den Wert übergeben und die Kopie ignorieren, wenn Sie sie nicht benötigen, oder als Referenz übergeben und die Kopie erstellen, wenn Sie sie benötigen.
Wenn Sie sich in einer Situation befinden, in der dies wirklich wichtig ist, können Sie zwei Überladungen bereitstellen, eine mit einer Konstantenwertreferenz und eine mit einer Wertreferenz. Eine Kopie, die andere bewegt sich. Eine perfekte Weiterleitungsfunktionsvorlage ist eine weitere Option.
quelle
shared_ptr<T> ptr;
(dhvoid f(T&)
mit aufgerufenf(*ptr)
) übergeben werden und das Objekt den Aufruf nicht überlebt. Alternativ schreiben Sie eine, bei der Sie an Wertvoid f(T)
und Fehler vorbeikommen .void f(T&)
und beinhaltet Threads. Ich denke, mein Problem ist, dass der Ton Ihrer Antwort davon abhält, das Eigentum an shared_ptrs zu übergeben. Dies ist die sicherste, intuitivste und am wenigsten komplizierte Art, sie zu verwenden. Ich würde es extrem ablehnen, einem Anfänger jemals zu raten, einen Verweis auf Daten zu extrahieren, die einem shared_ptr <> gehören. Die Möglichkeiten für Missbrauch sind groß und der Nutzen ist minimal.Ich denke, die Leute haben unnötig Angst davor, rohe Zeiger als Funktionsparameter zu verwenden. Wenn die Funktion den Zeiger nicht speichern oder seine Lebensdauer auf andere Weise beeinflussen soll, funktioniert ein Rohzeiger genauso gut und repräsentiert den kleinsten gemeinsamen Nenner. Überlegen Sie zum Beispiel, wie Sie a
unique_ptr
an eine Funktion übergeben würden, die ashared_ptr
als Parameter verwendet, entweder nach Wert oder nach const-Referenz?Ein Rohzeiger als Funktionsparameter hindert Sie nicht daran, intelligente Zeiger im aufrufenden Code zu verwenden, wo es wirklich darauf ankommt.
quelle
DoSomething(*a_smart_ptr)
fun(&x)
mitfun(x)
. Im letzteren Beispielx
könnte entweder als Wert oder als const ref übergeben werden. Wie oben erwähnt, können Sie auch bestehen,nullptr
wenn Sie nicht an der Ausgabe interessiert sind ...Ja, die gesamte Idee eines shared_ptr <> besteht darin, dass mehrere Instanzen denselben Rohzeiger enthalten können und der zugrunde liegende Speicher nur freigegeben wird, wenn dort die letzte Instanz von shared_ptr <> zerstört wird.
Ich würde einen Zeiger auf einen shared_ptr <> vermeiden, da dies den Zweck zunichte macht, da Sie sich jetzt wieder mit raw_pointers beschäftigen.
quelle
Das Übergeben von Werten in Ihrem ersten Beispiel ist sicher, aber es gibt eine bessere Redewendung. Wenn möglich an const-Referenz übergeben - ich würde ja sagen, auch wenn es sich um intelligente Zeiger handelt. Ihr zweites Beispiel ist nicht gerade kaputt, aber es ist sehr
!???
. Dumm, nichts zu erreichen und einen Teil des Punktes der intelligenten Zeiger zu besiegen, und Sie in einer fehlerhaften Welt voller Schmerzen zurückzulassen, wenn Sie versuchen, Dinge zu dereferenzieren und zu modifizieren.quelle
shared_ptr<>
insbesondere, dass Sie eine Kopie erstellen müssen , um das Eigentum tatsächlich zu teilen; Wenn Sie einen Verweis oder Zeiger auf ashared_ptr<>
haben, teilen Sie nichts und unterliegen denselben lebenslangen Problemen wie ein normaler Verweis oder Zeiger.shared_ptr
des Aufrufers überdauert garantiert den Funktionsaufruf, sodass die Funktion bei der Verwendung von sicher istshared_ptr<> const &
. Wenn der Zeiger für einen späteren Zeitpunkt gespeichert werden muss, muss er kopiert werden (zu diesem Zeitpunkt muss eine Kopie erstellt werden, anstatt eine Referenz zu enthalten). Die Kosten für das Kopieren müssen jedoch nicht anfallen, es sei denn, Sie müssen dies tun es. Ich würde in diesem Fall eine ständige Referenz geben ...shared_ptr
von der Schnittstelle entfernen und einen einfachen (const
) Verweis auf das spitze Objekt übergeben.shared_ptr<>
? Jetzt ist Ihre API wirklich nur ein Schmerz im Nacken, da sie den Aufrufer dazu zwingt, die Semantik der Objektlebensdauer sinnlos zu ändern, nur um sie zu verwenden. Ich sehe darin nichts, was es wert wäre, befürwortet zu werden, außer zu sagen: "Es tut technisch nichts weh." Ich sehe keine kontroversen Aussagen zu "Wenn Sie kein gemeinsames Eigentum benötigen, verwenden Sie es nichtshared_ptr<>
".In Ihrer Funktion
DoSomething
ändern Sie ein Datenelement einer Klasseninstanz. Sie ändernmyClass
also das verwaltete Objekt (Rohzeiger) und nicht das Objekt (shared_ptr). Dies bedeutet, dass am Rückgabepunkt dieser Funktion alle gemeinsam genutzten Zeiger auf den verwalteten Rohzeiger ihr Datenelement sehen:myClass::someField
geändert auf einen anderen Wert.In diesem Fall übergeben Sie ein Objekt an eine Funktion mit der Garantie, dass Sie es nicht ändern (es handelt sich um shared_ptr, nicht um das eigene Objekt).
Die Redewendung, um dies auszudrücken, ist durch: ein const ref, wie so
void DoSomething(const std::shared_ptr<myClass>& arg)
Ebenso versichern Sie dem Benutzer Ihrer Funktion, dass Sie der Liste der Eigentümer des Rohzeigers keinen weiteren Eigentümer hinzufügen. Sie haben jedoch die Möglichkeit beibehalten, das zugrunde liegende Objekt zu ändern, auf das der Rohzeiger zeigt.
CAVEAT: Das heißt, wenn auf irgendeine Weise jemand anruft,
shared_ptr::reset
bevor Sie Ihre Funktion aufrufen, und in diesem Moment der letzte shared_ptr ist, der den raw_ptr besitzt, wird Ihr Objekt zerstört und Ihre Funktion manipuliert einen baumelnden Zeiger auf ein zerstörtes Objekt. SEHR GEFÄHRLICH!!!quelle