Gibt es eine Standardmethode, um anzuzeigen, dass eine Funktion einen neuen Zeiger zurückgibt?

8

Manchmal möchte ich die Konstruktion von Objekten, die eine Klasse besitzt, an eine separate Funktion delegieren. Etwas wie

Vertex* new_vertex(const Options& options) {
  // do stuff...
  return new Vertex(...);
}

wobei die Funktion nur innerhalb einer Klasse verwendet werden soll, die die besitzt Vertex. Es ist klar, dass diese Funktion zu Verwirrung führen kann, daher möchte ich sie so klar wie möglich machen. Gibt es eine Namenskonvention für solche Funktionen?

Schaf
quelle
11
Ja. // TODO: Fix allocation of raw pointer.
Gort den Roboter
3
@StevenBurnap Ich würde das Wort "fix" nicht verwenden, wenn das Ding nicht funktioniert.
user253751
1
Nur eine Bemerkung zum Anliegen der "Verwirrung". Ebenso wichtig ist, dass der Anrufer das zurückgegebene Objekt ordnungsgemäß pflegt. Intelligente Zeiger können dies mühelos machen (indem sie die Speicherverwaltung eher zu einer mechanischen Gewohnheit als zu einer kontinuierlichen Anstrengung geistiger Anstrengung machen). Wenn der Anrufer jedoch keine klaren Richtlinien oder Codierungsstandards oder keine Vorstellung von hierarchischem "Objektbesitz" hat, wird dies letztendlich der Fall sein immer noch in Schwierigkeiten geraten. In einigen Fällen versuchen Junior-Programmierer sogar, das zu umgehen, unique_ptrindem sie seine release()Funktion aufrufen , und verwenden die rohen Zeiger wie auf die alte Weise.
Rwong
1
@StevenBurnap Warum nicht einfach // FIXME: Allocation of raw pointer?
Tobias Kienzler
1
Ihre Funktion macht es mir ziemlich klar. Der Rückgabetyp ist kein Wert oder eine Referenz - beides würde den Versuch, den Speicher zu löschen, zu einem nicht instinktiven syntaktischen Streit machen. Die Funktion wird aufgerufen, new_vertexdamit ich weiß, dass das Objekt neu geprägt wurde. Man könnte es Create_new_vertexals besonders klar bezeichnen. Was die Idee betrifft, dass Sie den Heap-Speicher nicht ohne intelligente Zeiger verwalten sollten, haben Sie nie die Wahrheit darin gesehen. Wenn Sie den Heap-Speicher ohne sie nicht verwalten können, haben Sie auch kein Geschäft damit, den Heap-Speicher mit ihnen zu verwalten!
Grimm The Opiner

Antworten:

21

Rückgabe a unique_ptr:

std::unique_ptr<Vertex> new_vertex(const Options& options) {
  // do stuff...
  return std::make_unique<Vertex>(...);
}

Es kann immer nur einen geben unique_ptr, der auf ein bestimmtes Objekt zeigt (es sei denn, Sie missbrauchen es, indem Sie es auf ein Vertex*und zurück werfen ). Sie können niemals eine kopieren unique_ptr, sondern nur verschieben. Wenn a unique_ptrzerstört wird (und nicht entfernt wurde), ist es sogar deletedas Objekt für Sie.

make_uniqueerstellt ein neues Vertexund hüllt es in ein unique_ptr; Die Argumente, an die Sie übergeben, make_uniquesind die Argumente, die an den Konstruktor übergeben werden.

user253751
quelle
4
Das ist hilfreich, beantwortet aber meine Frage nicht wirklich . Ich kenne mich mit intelligenten Zeigern aus und wollte nur wissen, ob es eine Namenskonvention für Funktionen gibt, die unformatierte Zeiger zurückgeben, um die sich der Benutzer kümmern muss. Ich denke die Antwort ist "nein, gibt es nicht, weil du das niemals tun solltest"?
Shep
3
@Shep Wenn Sie der Person, die Ihre Funktion aufruft, anzeigen möchten, dass sie ein neues Objekt erstellt, tun Sie unique_ptrdies. Wenn es aus irgendeinem Grund ein Name sein muss, dann denke ich nicht, aber warum muss es ein Name sein?
user253751
8
@ Shep-Namen sind immaterielle Dinge und jeder kann sie vollständig ignorieren. Typen hingegen werden vom Compiler erzwungen, was bedeutet, dass ein erheblicher Aufwand erforderlich ist, um Inhalte zu beschädigen. Senden Sie niemals einen Namen, um die Arbeit eines Typs zu erledigen.
ComicSansMS
1
@ Shep In diesem Fall kann es nicht schaden, einen Rohzeiger zurückzugeben . In diesem Fall haben Sie ein Objekt erstellt, dessen Lebensdauer vom Aufrufer verwaltet werden muss. Der Anrufer kann es jederzeit direkt in einen intelligenten Zeiger ablegen, wenn er dies für erforderlich hält.
Grimm The Opiner
1
@immibis: Warum überschreibst du den Template-Argument-Abzug von std::make_unique? Das ist unnötig ausführlich und fehleranfällig ...
Deduplicator
2

Gibt es eine Standardmethode, um anzuzeigen, dass eine Funktion einen neuen Zeiger zurückgibt?

Nein, es gibt keinen "Standardweg" (aber es gibt eine API-Entwurfsrichtlinie, die derzeit als "Best Practice" gilt).

Aufgrund dieser Mehrdeutigkeit ("Was möchte eine Funktion, die einen Zeiger zurückgibt, von mir?") Wird es derzeit als bewährte Methode angesehen, die Lebensdauer- und Eigentumsrichtlinie über den Rückgabetyp festzulegen:

<vertex pointer type> new_vertex(const Options& options);

<vertex pointer type>kann sein std::unique_ptr("new_vertex besitzt den Zeiger nicht") oder std::shared_ptr("Clientcode besitzt den Zeiger nicht") oder etwas anderes, das eine klar definierte Besitzersemantik aufweist (z. B. Vertex const * constwürde dem Clientcode anzeigen ", lesen Sie den Adresse und Werte, aber weder ändern noch den Zeiger löschen ").

Im Allgemeinen sollten Sie keinen rohen Zeiger zurückgeben (in einigen Fällen schlägt "Praktikabilität die Reinheit").

TLDR : Es gibt eine bewährte Methode ( ja ), aber keine Standardmethode (in der Sprache).

Bearbeiten :

Dabei soll die Funktion nur innerhalb einer Klasse verwendet werden, der der Vertex gehört

Wenn die Klasse den Vertex besitzt, würde ich ihn so schreiben:

class SomeClass // owns the vertex
{
    void do_stuff() // uses the vertex internally
    {
        init_vertex(); // see below
        assert(vertex.get());
        // use vertex as needed
    }
private:
    // "class owns the Vertex"
    std::unique_ptr<Vertex> vertex;

    // sets the internal vertex
    // function doesn't return a pointer of any kind
    void init_vertex(const Options& options); // or "reset_", "refresh_", 
                                              // or "make_" vertex,
                                              // if the pointer can change
                                              // throughout the lifetime
                                              // of a SomeClass instance
};
utnapistim
quelle
0

Ja, es gibt Dutzende von "Standards"

XKCD

Die empfohlene Antwort unique_ptrist wahrscheinlich die beste Antwort, aber wenn Sie sich rohe Zeiger ansehen, hat jeder seinen eigenen Standard entwickelt.

Im allgemeinen Funktion mit dem Namen create_____oder new______oder alloc_____neigt dazu , ein neues Objekt zu implizieren.

jedoch

Bedenken Sie, dass Sie Verhaltensweisen mischen. Es ist Ihnen eigentlich egal, ob dies eine neue Instanz eines Objekts ist oder nicht. Was Sie interessiert, ist, ob der Anrufer für die Zerstörung des Objekts verantwortlich ist oder nicht. Es gibt viele APIs, die die Verantwortung für ein vorhandenes Objekt übertragen, und diese Funktionen benötigen ein übereinstimmendes Namensschema. Vielleicht give______.

Wenn Sie bereit sind, sich ein wenig von C ++ zu entfernen ...
Wenn Sie bereit sind, Objective-C als C ++ ähnlich genug zu betrachten, um eine Antwortquelle zu finden, gibt es in dieser Sprache tatsächlich eine echte Antwort. Objective-C verwaltet die Lebensdauer von Objekten mithilfe von Referenzzählern. Sie brauchten eine Möglichkeit zu beschreiben, ob Ihnen die Verantwortung für ein Objekt übertragen wurde, oder lediglich einen Verweis auf ein vorhandenes Objekt. Wenn Sie die Antwort falsch verstehen, erhalten Sie die Ref-Zählungen falsch und verlieren Objekte. Dementsprechend haben sie eine Regel aufgestellt: Jedes Objekt, das Sie zurückgeben, gehört einer anderen Person (Sie müssen also die Ref-Anzahl selbst erhöhen und verringern), mit Ausnahme von Erstellungsaufrufen, die einen bereits inkrementierten Zeiger an Sie übergeben (Sie müssen nur dekrementieren). Diese Aufrufe wurden durch den Methodennamen identifiziert. Methoden beginnend mit alloc new copyodermutableCopyhat immer ein neues Objekt zurückgegeben. Dort wurde der Standard also von der Sprache durchgesetzt.

Cort Ammon
quelle