Devo usar shared_ptr ou unique_ptr

Eu tenho feito alguns objects usando o idioma pimpl, mas não tenho certeza se deve usar std::shared_ptr ou std::unique_ptr .

Eu entendo que std::unique_ptr é mais eficiente, mas isso não é um grande problema para mim, já que esses objects são relativamente pesados ​​de qualquer maneira, então o custo de std::shared_ptr sobre std::unique_ptr é relativamente pequeno.

Atualmente estou indo com std::shared_ptr apenas por causa da flexibilidade extra. Por exemplo, usar um std::shared_ptr permite armazenar esses objects em um hashmap para access rápido enquanto ainda é possível retornar cópias desses objects para os chamadores (já que acredito que quaisquer iteradores ou referências podem se tornar inválidos rapidamente).

No entanto, esses objects não são realmente copiados, já que as alterações afetam todas as cópias, então eu estava imaginando que talvez usar std::shared_ptr e permitir cópias seja algum tipo de anti-padrão ou algo ruim.

Isso está correto?

Eu tenho feito alguns objects usando o idioma pimpl, mas não tenho certeza se usou shared_ptr ou unique_ptr .

Definitivamente unique_ptr ou scoped_ptr .

Pimpl não é um padrão, mas um idioma, que lida com dependência em tempo de compilation e compatibilidade binária. Não deve afetar a semântica dos objects, especialmente no que diz respeito ao seu comportamento de cópia.

Você pode usar qualquer tipo de ponteiro inteligente que desejar, mas esses dois garantem que você não compartilhará acidentalmente a implementação entre dois objects distintos, já que eles exigem uma decisão consciente sobre a implementação do construtor de cópias e do operador de atribuição.

No entanto, esses objects não são realmente copiados, já que as alterações afetam todas as cópias, então eu estava imaginando que talvez usar shared_ptr e permitir cópias seja algum tipo de anti-padrão ou coisa ruim.

Não é um antipadrão, na verdade, é um padrão: aliasing. Você já o usa, em C ++, com pointers e referências simples. shared_ptr oferecem uma medida extra de “segurança” para evitar referências mortas, ao custo de complexidade extra e novos problemas (cuidado com ciclos que criam vazamentos de memory).


Sem relação com o Pimpl

Eu entendo unique_ptr é mais eficiente, mas isso não é tanto um problema para mim, como esses objects são relativamente pesados ​​de qualquer maneira, o custo de shared_ptr over unique_ptr é relativamente menor.

Se você puder fatorar algum estado, você pode querer dar uma olhada no padrão Flyweight .

Se você usa shared_ptr , não é realmente o clássico pimpl idiom (a menos que você tome medidas adicionais). Mas a verdadeira questão é por que você quer usar um ponteiro inteligente para começar; É muito claro onde a delete deve ocorrer, e não há nenhuma questão de segurança de exceção ou outra para se preocupar. No máximo, um ponteiro inteligente economizará uma ou duas linhas de código. E o único que tem a semântica correta é boost::scoped_ptr , e eu não acho que funciona neste caso. (IIRC, requer um tipo completo para ser instanciado, mas eu posso estar errado.)

Um aspecto importante do idioma pimpl é que seu uso deve ser transparente para o cliente; a class deve se comportar exatamente como se fosse implementada classicamente. Isso significa inibir a cópia e a atribuição ou implementar a cópia profunda, a menos que a class seja imutável (nenhuma function membro não-constante). Nenhum dos pointers inteligentes usuais implementa a cópia profunda; você poderia implementar um, é claro, mas provavelmente ainda exigiria um tipo completo sempre que a cópia ocorresse, o que significa que você ainda teria que fornecer um construtor de cópia definido pelo usuário e um operador de atribuição (já que eles não podem estar em linha). Perante isto, provavelmente não vale a pena usar o ponteiro inteligente.

Uma exceção é se os objects forem imutáveis. Nesse caso, não importa se a cópia é profunda ou não, e shared_ptr cuida da situação completamente.

Quando você usa um shared_ptr (por exemplo, em um contêiner, em seguida, procure-o e retorne-o por valor ), você não está causando uma cópia do object para o qual aponta, simplesmente uma cópia do ponteiro com uma contagem de referência.

Isso significa que, se você modificar o object subjacente de vários pontos, afetará as alterações na mesma instância . Isto é exatamente o que é projetado, então não é um anti-padrão !

Ao passar um shared_ptr (como dizem os comentários), é melhor passar pela referência const e copiar (lá incrementando a contagem de referência) onde necessário. Quanto ao retorno, caso a caso.

Sim, por favor, use-os. Simplificando, o shared_ptr é uma implementação do ponteiro inteligente. unique_ptr é uma implementação do ponteiro automático: