¿Hay alguna forma estándar de indicar que una función devuelve un nuevo puntero?

8

A veces quiero delegar la construcción de objetos que posee una clase a una función separada. Algo como

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

donde la función solo está destinada a ser utilizada dentro de una clase que posee el Vertex. Claramente, esta función puede causar cierta confusión de pérdida de memoria, por lo que quiero dejarlo lo más claro posible. ¿Existe una convención de nomenclatura para tales funciones?

Shep
fuente
11
Si. // TODO: Fix allocation of raw pointer.
Gort the Robot
3
@StevenBurnap No usaría la palabra "arreglar" a menos que la cosa no funcione.
user253751
1
Solo un comentario sobre la preocupación de la "confusión". Es igualmente importante que la persona que llama cuide adecuadamente el objeto devuelto. Los punteros inteligentes pueden hacer esto sin esfuerzo (al hacer que la gestión de la memoria sea un hábito mecánico en lugar de un esfuerzo continuo de esfuerzo mental), pero si la persona que llama no tiene una política clara o un estándar de codificación o una noción de "propiedad de objeto" jerárquica, eventualmente lo hará. Todavía tengo problemas. En algunos casos, los programadores junior pueden incluso tratar de eludirlo unique_ptrllamando a su release()función, y usar los punteros en bruto como en las viejas formas.
rwong
1
@StevenBurnap ¿Por qué no simplemente // FIXME: Allocation of raw pointer?
Tobias Kienzler
1
Tu función me lo deja bastante claro. El tipo de retorno no es por valor o una referencia, lo que haría que tratar de limpiar la memoria sea una disputa sintáctica no instintiva. Se llama a la función, new_vertexasí que sé que el objeto está recién acuñado. Podrías llamarlo Create_new_vertexpara ser más claro. En cuanto a la idea de que no se debe gestionar la memoria heap sin punteros inteligentes, nunca había visto la verdad porque - de hecho, si usted no puede manejar la pila de memoria y sin ellos, no tienes negocio la gestión de memoria del montón con ellos, ya sea!
Grimm The Opiner

Respuestas:

21

Devolver a unique_ptr:

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

Solo puede haber uno que unique_ptrapunte a un objeto determinado (a menos que lo maltrate arrojándolo hacia Vertex*atrás y hacia atrás, de todos modos). Nunca puedes copiar un unique_ptr, solo moverlo. Cuando a unique_ptrse destruye (y no se ha eliminado), incluso deletees el objeto para usted.

make_uniquecrea un nuevo Vertexy lo envuelve en un unique_ptr; los argumentos que pasa make_uniqueson los argumentos que pasa al constructor.

usuario253751
fuente
44
Esto es útil, pero en realidad no responde a mi pregunta. Sé acerca de los punteros inteligentes, solo quería saber si existe una convención de nomenclatura para las funciones que devuelven punteros sin procesar de los que el usuario tiene que ocuparse. Supongo que la respuesta es "no, no la hay, porque nunca deberías hacer eso".
Shep
3
@Shep Si desea indicar a la persona que llama a su función que crea un nuevo objeto, unique_ptrhaga eso. Si tiene que ser un nombre por alguna razón, entonces supongo que no, pero ¿por qué tiene que ser un nombre?
user253751
8
@ Los nombres Shep son cosas intangibles y todos son libres de ignorarlos por completo. Los tipos, por otro lado, son aplicados por el compilador, lo que significa que requiere un esfuerzo significativo para romper cosas. Nunca envíe un nombre para hacer el trabajo de un tipo.
ComicSansMS
1
@ Sheep no hay ningún daño en devolver un puntero sin formato en este caso . Este caso es que ha creado un objeto que depende de la persona que llama administrar la vida útil de. La persona que llama siempre puede soltarlo directamente en un puntero inteligente si cree que lo necesita.
Grimm The Opiner
1
@immibis: ¿Por qué anular la deducción de argumentos de plantilla de std::make_unique? Eso es innecesariamente detallado y propenso a errores ...
Deduplicator
2

¿Hay alguna forma estándar de indicar que una función devuelve un nuevo puntero?

No, no existe una "forma estándar" (pero hay una política de diseño de API actualmente considerada "mejor práctica").

Debido a esta ambigüedad ("¿qué quiere que haga una función que devuelve un puntero?"), Actualmente se considera la mejor práctica imponer la política de vida y propiedad, a través del tipo de devolución:

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

<vertex pointer type>puede ser std::unique_ptr("new_vertex no posee el puntero"), o std::shared_ptr("el código del cliente no posee el puntero"), o algo más, que tenga una semántica de propiedad claramente definida (por ejemplo, Vertex const * constindicaría al código del cliente "leer el dirección y valores, pero no cambie ninguno / no elimine el puntero ").

En general, no debe devolver un puntero sin formato (pero en algunos casos, "la practicidad es mejor que la pureza").

TLDR : hay una mejor práctica ( ), pero no una forma estándar (en el idioma).

Editar :

donde la función solo está destinada a ser utilizada dentro de una clase que posee Vertex

Si la clase posee el Vértice, lo escribiría así:

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
fuente
0

Sí, hay docenas de "estándares" sobre esto

XKCD

La respuesta recomendada unique_ptres probablemente la mejor respuesta, pero si está buscando punteros en bruto, todos han desarrollado su propio estándar.

En términos generales las funciones nombradas create_____o new______o alloc_____tienden a implicar un nuevo objeto.

sin embargo

Considera que estás mezclando comportamientos. Realmente no le importa si esta es una nueva instancia de un objeto o no. Lo que le importa es si la persona que llama es responsable de destruir el objeto o no. Hay muchas API que transfieren la responsabilidad de un objeto existente, y esas funciones necesitarán un esquema de nomenclatura coincidente. Tal vez give______.

Si está dispuesto a alejarse un poco de C ++ ...
Si está dispuesto a considerar que Objective-C es lo suficientemente similar a C ++ como fuente de respuesta, en realidad hay una respuesta real en ese lenguaje. Objective-C gestiona la vida útil de los objetos utilizando recuentos de referencia. Necesitaban una manera de describir si se le estaba dando la responsabilidad de un objeto, o simplemente una referencia a un objeto existente. Si obtienes la respuesta incorrecta, obtienes los recuentos de referencia incorrectos y pierdes objetos. En consecuencia, establecieron una regla: cada objeto que devuelve es propiedad de otra persona (por lo que debe aumentar y disminuir el recuento de referencia usted mismo), excepto las llamadas de creación que le pasan un puntero ya incrementado (solo tiene que disminuir). Estas llamadas fueron identificadas por el nombre del método. Métodos que comienzan con alloc new copyomutableCopysiempre devuelve un nuevo objeto. Entonces, el estándar hizo cumplir el estándar.

Cort Ammon
fuente