Existe uma maneira padrão de indicar que uma função retorna um novo ponteiro?

8

Às vezes, quero delegar a construção de objetos que uma classe possui para uma função separada. Algo como

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

onde a função se destina apenas a ser usada dentro de uma classe que possui o Vertex. Claramente, essa função pode causar alguma confusão de vazamento de memória, por isso quero deixar o mais claro possível. Existe uma convenção de nomenclatura para essas funções?

Shep
fonte
11
Sim. // TODO: Fix allocation of raw pointer.
Gort the Robot
3
@StevenBurnap Eu não usaria a palavra "correção" a menos que a coisa não funcione.
user253751
1
Apenas uma observação sobre a preocupação de "confusão". É igualmente importante que o chamador cuide adequadamente do objeto retornado. Indicadores inteligentes podem tornar isso fácil (tornando o gerenciamento de memória um hábito mecânico em vez de um esforço contínuo de esforço mental), mas se o chamador não tiver uma política clara ou padrão de codificação ou noção de "propriedade de objeto" hierárquica, eventualmente ainda tem problemas. Em alguns casos, programadores juniores podem até tentar contornar a unique_ptrchamada de sua release()função e usar os ponteiros brutos como nos velhos métodos.
Rwong 15/07/2015
1
@StevenBurnap Por que não simplesmente // FIXME: Allocation of raw pointer?
Tobias Kienzler
1
Sua função deixa bem claro para mim. O tipo de retorno não é por valor ou por referência - qualquer um deles tornaria a tentativa de esclarecer a memória uma disputa sintática não instintiva. A função é chamada, new_vertexentão eu sei que o objeto foi cunhado recentemente. Você poderia chamá-lo Create_new_vertexpara ficar mais claro. Quanto à idéia de que você não deve gerenciar a memória heap sem ponteiros inteligentes, nunca viu a verdade nisso - na verdade, se você não pode gerenciar a memória heap sem eles, também não há negócios em gerenciar a memória heap com eles!
Grimm The Opiner

Respostas:

21

Retornar a unique_ptr:

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

Só pode haver um que unique_ptraponte para um determinado objeto (a menos que você o abuse ao transmitir para um Vertex*e para trás, de qualquer maneira). Você nunca pode copiar um unique_ptr, apenas movê-lo. Quando um unique_ptré destruído (e não foi removido), ele deleteé o objeto para você.

make_uniquecria um novo Vertexe envolve-o em um unique_ptr; os argumentos aos quais você passa make_uniquesão os argumentos que passa para o construtor.

user253751
fonte
4
Isso é útil, mas realmente não responde à minha pergunta. Eu sei sobre ponteiros inteligentes, só queria saber se existe uma convenção de nomenclatura para funções que retornam ponteiros brutos que o usuário precisa cuidar. Eu acho que a resposta é "não, não existe, porque você nunca deve fazer isso"?
Shep
3
@Shep Se você quiser indicar à pessoa que está chamando sua função que ela cria um novo objeto, unique_ptrfaça isso. Se tem que ser um nome por algum motivo, acho que não, mas por que tem que ser um nome?
user253751
8
Os nomes @Shep são coisas intangíveis e todos são livres para ignorá-los completamente. Os tipos, por outro lado, são aplicados pelo compilador, o que significa que exige um esforço significativo para quebrar as coisas. Nunca envie um nome para fazer o trabalho de um tipo.
ComicSansMS
1
@ Shep, não há nenhum mal em retornar um ponteiro bruto neste caso . Nesse caso, você criou um objeto que cabe ao chamador gerenciar o tempo de vida de. O chamador sempre pode colocá-lo diretamente em um ponteiro inteligente, se achar necessário.
Grimm The Opiner
1
@immibis: Por que você substitui a dedução de argumento de modelo std::make_unique? Isso é unneccessarily detalhado e propenso a erros ...
Deduplicator
2

Existe uma maneira padrão de indicar que uma função retorna um novo ponteiro?

Não, não existe uma "maneira padrão" (mas há uma política de design de API atualmente considerada "prática recomendada").

Devido a essa ambiguidade ("o que uma função que retorna um ponteiro quer que eu faça com ela?"), Atualmente é considerado uma boa prática impor a política de duração e propriedade, por meio do tipo de retorno:

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

<vertex pointer type>pode ser std::unique_ptr("new_vertex não possui o ponteiro") ou std::shared_ptr("o código do cliente não possui o ponteiro") ou qualquer outra coisa que tenha definido claramente a semântica de propriedade (por exemplo, Vertex const * constindicaria para o código do cliente "leia o endereço e valores, mas não altere nem exclua o ponteiro ").

Geralmente, você não deve retornar um ponteiro bruto (mas, em alguns casos, "a praticidade supera a pureza").

TLDR : existe uma prática recomendada ( sim ), mas não uma maneira padrão (no idioma).

Editar :

onde a função se destina apenas a ser usada dentro de uma classe que possui o Vertex

Se a classe possuir o Vertex, eu escreveria assim:

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

Sim, existem dezenas de "padrões" sobre isso

XKCD

A resposta que recomenda unique_ptré provavelmente a melhor resposta, mas se você estiver olhando para indicadores brutos, todos desenvolveram seu próprio padrão.

De um modo geral funções de chamada create_____ou new______ou alloc_____tendem a implicar um novo objeto.

Contudo

Considere que você está misturando comportamentos. Você realmente não se importa se essa é uma nova instância de um objeto ou não. O que importa é se o chamador é responsável por destruir o objeto ou não. Existem muitas APIs que assumem a responsabilidade por um objeto existente e essas funções precisarão de um esquema de nomenclatura correspondente. Talvez give______.

Se você deseja se afastar um pouco do C ++ ...
Se você deseja considerar o Objective-C como semelhante ao C ++ como fonte de resposta, na verdade há uma resposta real nessa linguagem. Objective-C gerencia a vida útil dos objetos usando contagens de referência. Eles precisavam de uma maneira de descrever se você estava sendo responsabilizado por um objeto ou apenas uma referência a um objeto existente. Se você errar a resposta, errará as contagens de referências e perderá objetos. Dessa forma, eles estabeleceram uma regra: todo objeto retornado pertence a outra pessoa (portanto, você deve aumentar e diminuir a contagem de referências), exceto as chamadas de criação que passam um ponteiro já incrementado para você (você só precisa diminuir). Essas chamadas foram identificadas pelo nome do método. Métodos começando com alloc new copyoumutableCopysempre retornava um novo objeto. Então, o padrão foi imposto pelo idioma.

Cort Ammon
fonte