wake-up-neo.com

Gemeinsame Zeiger als Argumente übergeben

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 Genannte einfach den Referenzzähler 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 2. Weg effizienter ist, weil er nur 1 Adresse kopieren muss (im Gegensatz zum gesamten Smart Pointer), aber der 1. Weg scheint besser lesbar zu sein und ich erwarte nicht, die Leistungsgrenzen zu überschreiten. Ich möchte nur sicherstellen, dass es nichts Gefährliches gibt.

Vielen Dank.

79
Steve H

Ich möchte einen gemeinsamen Zeiger auf eine Funktion übergeben. Kannst du mir damit helfen?

Klar, ich kann dir dabei helfen. Ich gehe davon aus, dass Sie einige Kenntnisse über die Besitzersemantik in C++ haben. Ist das wahr?

Ja, ich fühle mich einigermaßen wohl mit dem Thema.

Gut.

Ok, ich kann mir nur zwei Gründe vorstellen, um ein shared_ptr Argument anzunehmen:

  1. Die Funktion möchte das Eigentum an dem Objekt teilen.
  2. Die Funktion führt eine Operation aus, die speziell für shared_ptr Funktioniert.

Für wen interessieren Sie sich?

Ich bin auf der Suche nach einer allgemeinen Antwort und bin an beiden interessiert. Ich bin gespannt, was du mit Fall 2 meinst.

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.

Ah, wenn die Funktion tatsächlich den Smart Pointer selbst manipulieren muss.

Genau.

In diesem Fall sollten wir meiner Meinung nach als Referenz dienen.

Ja. Wenn sich der Zeiger nicht ändert, möchten Sie einen konstanten Verweis übergeben. Sie müssen keine Kopien anfertigen, da Sie den Besitz nicht teilen müssen. Das ist das andere Szenario.

OK habe es. Sprechen wir über das andere Szenario.

Den, an dem Sie beteiligt sind? Okay. Wie teilt man den Besitz mit shared_ptr?

Durch das Kopieren.

Dann muss die Funktion eine Kopie von shared_ptr Erstellen, richtig?

Offensichtlich. Also übergebe ich es durch einen Verweis auf const und kopiere in eine lokale Variable?

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.

Guter Punkt. Ich muss mich öfter an den Artikel " Willst du Geschwindigkeit? Übergebe den Wert. " erinnern.

Warten Sie, was ist, wenn die Funktion zum Beispiel den shared_ptr In einer Mitgliedsvariablen speichert? Macht das nicht eine überflüssige Kopie?

Die Funktion kann einfach das Argument shared_ptr In ihren Speicher verschieben. Das Verschieben eines shared_ptr Ist billig, da es keine Referenzzahlen ändert.

Ah, gute Idee.

Aber ich denke an ein drittes Szenario: Was ist, wenn Sie weder den shared_ptr Manipulieren noch die Eigentumsrechte teilen möchten?

In diesem Fall ist shared_ptr 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.

Und sollte ich den Spitzenwert als Referenz oder als Wert nehmen?

Es gelten die üblichen Regeln. Intelligente Zeiger ändern nichts.

Übergeben Sie den Wert, wenn Sie kopieren möchten, und den Verweis, wenn Sie eine Kopie vermeiden möchten.

Recht.

Hmm. Ich denke, Sie haben noch ein weiteres Szenario vergessen. Was ist, wenn ich das Eigentum teilen möchte, aber nur in Abhängigkeit von einer bestimmten Bedingung?

Ah, ein interessanter Edge-Fall. Ich erwarte nicht, dass das oft passiert. In diesem Fall können Sie die Kopie entweder als Wert übergeben und ignorieren, wenn Sie sie nicht benötigen, oder als Referenz übergeben und die Kopie erstellen, wenn Sie sie benötigen.

Ich riskiere eine überflüssige Kopie in der ersten Option und verliere einen möglichen Zug in der zweiten. Kann ich den Kuchen nicht auch essen?

Wenn Sie sich in einer Situation befinden, in der das wirklich wichtig ist, können Sie zwei Überladungen bereitstellen, eine mit einer konstanten Wertreferenz und eine andere mit einer r-Wertreferenz. Einer kopiert, der andere bewegt sich. Eine perfekte Vorlage für die Weiterleitungsfunktion ist eine weitere Option.

Ich denke, das deckt alle möglichen Szenarien ab. Vielen Dank.

154

Ich glaube, die Leute haben unnötig Angst davor, rohe Zeiger als Funktionsparameter zu verwenden. Wenn die Funktion den Zeiger nicht speichert oder seine Lebensdauer auf andere Weise beeinflusst, funktioniert ein roher Zeiger genauso gut und stellt den kleinsten gemeinsamen Nenner dar. Stellen Sie sich zum Beispiel vor, wie Sie einen unique_ptr - Wert an eine Funktion übergeben, die einen shared_ptr - Wert als Parameter verwendet, entweder als Wert oder als Konstantenreferenz.

void DoSomething(myClass * p);

DoSomething(myClass_shared_ptr.get());
DoSomething(myClass_unique_ptr.get());

Ein roher Zeiger als Funktionsparameter hindert Sie nicht daran, intelligente Zeiger im aufrufenden Code zu verwenden, wo es wirklich darauf ankommt.

17
Mark Ransom

Ja, die gesamte Idee zu einem shared_ptr <> ist, dass mehrere Instanzen denselben unformatierten Zeiger 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 ein shared_ptr <> vermeiden, da dies den Zweck zunichte macht, da Sie sich jetzt wieder mit raw_pointers beschäftigen.

3

Wertübergabe in Ihrem ersten Beispiel ist sicher, aber es gibt eine bessere Redewendung. Übergeben Sie nach Möglichkeit einen konstanten Verweis - ich würde ja sagen, auch wenn es sich um intelligente Zeiger handelt. Ihr zweites Beispiel ist nicht genau gebrochen, aber es ist sehr !???. Dumm, nichts zu erreichen und einen Teil des Punktes der intelligenten Zeiger zu besiegen, und dich in eine buggy Welt der Schmerzen zu versetzen, wenn du versuchst, Dinge zu dereferenzieren und zu modifizieren.

2
djechlin