ti-enxame.com

Devo passar um shared_ptr por referência?

Quais são as melhores práticas para passar um shared_ptr?

Atualmente eu passo argumentos da função shared_ptr assim:

void function1( shared_ptr<TYPE>& value );
88
Corvusoft

Em circunstâncias controladas, você pode passar o ponteiro compartilhado por referência constante . Certifique-se de que ninguém esteja excluindo o objeto simultaneamente, embora isso não seja muito difícil se você tiver cuidado a quem você fornece referências.

Em geral, você deve passar o ponteiro compartilhado como uma cópia reta . Isso fornece sua semântica pretendida: Todo escopo que contém uma cópia do ponteiro compartilhado mantém o objeto ativo em virtude de seu "compartilhamento" na propriedade.

A única razão para não passar sempre por valor é que copiar um ponteiro compartilhado tem um certo preço devido à atualização da contagem de referência atômica; no entanto, isso pode não ser uma grande preocupação.


Digressão opcional:

Como a questão principal foi respondida, talvez seja instrutivo considerar algumas maneiras pelas quais você deve nunca usar um ponteiro compartilhado. Aqui está um pequeno experimento de pensamento. Vamos definir um tipo de ponteiro compartilhado SF = std::shared_ptr<Foo>. Para considerar referências, em vez de passar argumentos de função, vamos ver o tipo RSF = std::reference_wrapper<T>. Ou seja, se tivermos um ponteiro compartilhado SF p(std::make_shared<Foo>());, poderemos criar um wrapper de referência com semântica de valor via RSF w = std::ref(p);. Tanto para a configuração.

Agora, todo mundo sabe que recipientes de ponteiros são campos minados. Então std::vector<Foo*> será um pesadelo para ser mantido, e qualquer número de bugs surgirá de um gerenciamento inadequado da vida útil. O que é pior conceitualmente é que nunca fica claro quem possui os objetos cujos indicadores o container armazena. Os ponteiros podem até ser uma mistura de ponteiros para objetos dinâmicos, objetos automáticos e lixo. Ninguém pode dizer. Portanto, a solução padrão é usar std::vector<SF>. Este é o caminho certo para usar o ponteiro compartilhado. Por outro lado, o que você nunca deve usar é std::vector<RSF> - este é um monstro incontrolável que na verdade é muito parecido com o vetor original de ponteiros nus! Por exemplo, não está claro se o objeto para o qual você mantém uma referência ainda está ativo. Tomar uma referência do ponteiro compartilhado derrotou todo o seu propósito.

Para um segundo exemplo, suponha que tenhamos um ponteiro compartilhado SF p como antes. Agora temos uma função int foo(SF) que queremos executar simultaneamente. O usual std::thread(foo, p) funciona muito bem, já que o construtor de threads faz uma cópia dos seus argumentos. No entanto, se disséssemos std::thread(foo, std::ref(p)), estaríamos em todos os tipos de problemas: O ponteiro compartilhado no escopo de chamada poderia expirar e destruir o objeto, e você ficaria com uma referência pendente e um ponteiro inválido!

Espero que esses dois exemplos reconhecidamente bem planejados tenham um pouco de luz quando você realmente quer que seus ponteiros compartilhados sejam passados ​​por cópia . Em um programa bem projetado, deve ficar claro quem é responsável por quais recursos e, quando usado corretamente, o ponteiro compartilhado é uma ótima ferramenta para o trabalho.

95
Kerrek SB

Isso depende do que você quer. O participante deve compartilhar a propriedade do objeto? Então precisa de sua própria cópia do shared_ptr. Então passe por valor.

Se uma função simplesmente precisar acessar um objeto de propriedade do chamador, vá em frente e passe por (const) reference, para evitar a sobrecarga de copiar o shared_ptr.

A melhor prática em C++ é sempre ter uma semântica de propriedade claramente definida para seus objetos. Não há universal "sempre faça isso" para substituir o pensamento real .

Se você sempre = passar ponteiros compartilhados por valor, fica caro (porque é muito mais caro copiar do que um ponteiro bruto). Se você nunca fizer isso, então não há nenhum ponto em usar um ponteiro compartilhado em primeiro lugar.

Copie o ponteiro compartilhado quando uma nova função ou objeto precisar compartilhar a propriedade do pointee.

25
jalf

Passe por referência const se você passar por referência. Isso deixa claro que você está passando por ref por motivos de desempenho. Além disso, use make_shared quando puder, pois isso evita uma indireção, o que resulta em um aumento de desempenho.

8
Kate Gregory