Ok, então a última vez que escrevi C ++ para viver, std::auto_ptr
era tudo o que a lib std tinha disponível, e boost::shared_ptr
era toda a raiva. Eu realmente nunca olhei para os outros tipos de ponteiros inteligentes fornecidos. Entendo que o C ++ 11 agora fornece alguns dos tipos que o impulso propiciou, mas nem todos.
Então, alguém tem um algoritmo simples para determinar quando usar qual ponteiro inteligente? De preferência, incluindo conselhos sobre ponteiros mudos (como ponteiros brutos T*
) e o restante dos ponteiros inteligentes de impulso. (Algo como isso seria ótimo).
Respostas:
Propriedade compartilhada:
o
shared_ptr
eweak_ptr
o padrão adotado são praticamente os mesmos de seus colegas do Boost . Use-os quando precisar compartilhar um recurso e não souber qual será o último a estar vivo. Useweak_ptr
para observar o recurso compartilhado sem influenciar sua vida útil, para não interromper os ciclos. Ciclos comshared_ptr
normalmente não deveriam acontecer - dois recursos não podem ser proprietários um do outro.Observe que o Boost oferece adicionalmente
shared_array
, o que pode ser uma alternativa adequadashared_ptr<std::vector<T> const>
.Em seguida, o Boost oferece
intrusive_ptr
, que é uma solução leve, se o seu recurso já oferece gerenciamento com contagem de referência e você deseja adotá-lo no princípio RAII. Este não foi adotado pela norma.Propriedade exclusiva: o
Boost também possui um
scoped_ptr
, que não é copiável e para o qual você não pode especificar um deleter.std::unique_ptr
estáboost::scoped_ptr
em esteróides e deve ser sua escolha padrão quando você precisa de um ponteiro inteligente . Ele permite que você especifique um deleter em seus argumentos de modelo e é móvel , ao contrárioboost::scoped_ptr
. Também é totalmente utilizável em contêineres STL, desde que você não use operações que precisem de tipos copiáveis (obviamente).Observe novamente que o Boost possui uma versão de matriz:,
scoped_array
que é o padrão unificado por exigirstd::unique_ptr<T[]>
especialização parcial que fará com quedelete[]
o ponteiro seja substituídodelete
por ele (comdefault_delete
r).std::unique_ptr<T[]>
também oferece emoperator[]
vez deoperator*
eoperator->
.Observe que
std::auto_ptr
ainda está no padrão, mas está obsoleto .§D.10 [depr.auto.ptr]
Sem propriedade:
use ponteiros mudos (ponteiros brutos) ou referências para referências não proprietárias de recursos e quando souber que o recurso sobreviverá ao objeto / escopo de referência. Prefira referências e use ponteiros brutos quando precisar de nulidade ou redefinição.
Se você deseja uma referência não proprietária a um recurso, mas não sabe se o recurso sobreviverá ao objeto que o referencia, coloque o recurso em um
shared_ptr
e use aweak_ptr
- você pode testar se o paishared_ptr
está vivolock
, o que retorne umshared_ptr
que não seja nulo se o recurso ainda existir. Se quiser testar se o recurso está inoperante, useexpired
. Os dois podem parecer semelhantes, mas são muito diferentes diante da execução simultânea, poisexpired
apenas garantem seu valor de retorno para essa única instrução. Um teste aparentemente inocente comoé uma condição potencial de corrida.
fonte
shared_ptr
e o ponteiro que não possui. umweak_ptr
...shared_array<T>
é uma alternativa parashared_ptr<T[]>
nãoshared_ptr<vector<T>>
: não pode crescer.Decidir qual ponteiro inteligente usar é uma questão de propriedade . Quando se trata de gerenciamento de recursos, o objeto A possui o objeto B se estiver no controle da vida útil do objeto B. Por exemplo, as variáveis de membro pertencem aos seus respectivos objetos porque o tempo de vida das variáveis de membro está vinculado à vida útil do objeto. Você escolhe ponteiros inteligentes com base em como o objeto pertence.
Observe que a propriedade de um sistema de software é separada da propriedade, como poderíamos pensar fora do software. Por exemplo, uma pessoa pode "possuir" sua casa, mas isso não significa necessariamente que um
Person
objeto tenha controle sobre a vida útil de umHouse
objeto. Confundir esses conceitos do mundo real com os conceitos de software é uma maneira infalível de se programar em um buraco.Se você possui a propriedade exclusiva do objeto, use
std::unique_ptr<T>
.Se você compartilhou a propriedade do objeto ...
- Se não houver ciclos na propriedade, use
std::shared_ptr<T>
.- Se houver ciclos, defina uma "direção" e use
std::shared_ptr<T>
em uma direção estd::weak_ptr<T>
na outra.Se o objeto é seu, mas existe o potencial de não ter dono, use ponteiros normais
T*
(por exemplo, ponteiros pai).Se o objeto lhe pertence (ou tem existência garantida), use referências
T&
.Advertência: Esteja ciente dos custos de indicadores inteligentes. Em ambientes com desempenho limitado ou de memória, pode ser benéfico usar ponteiros normais com um esquema mais manual para gerenciar a memória.
Os custos:
std::shared_ptr
possui a sobrecarga de um incremento da contagem de referência na cópia, além de um decréscimo na destruição seguido de uma verificação de contagem de 0 com exclusão do objeto em espera. Dependendo da implementação, isso pode inchar seu código e causar problemas de desempenho.Exemplos:
Uma árvore binária não possui seu pai, mas a existência de uma árvore implica a existência de seu pai (ou
nullptr
raiz), de modo que usa um ponteiro normal. Uma árvore binária (com semântica de valores) possui propriedade exclusiva de seus filhos, de modo que sãostd::unique_ptr
.Aqui, o nó da lista possui suas listas seguintes e anteriores, portanto, definimos uma direção e usamos
shared_ptr
para next eweak_ptr
for prev para interromper o ciclo.fonte
shared_ptr<BinaryTree>
para os filhos eweak_ptr<BinaryTree>
para o relacionamento dos pais.Use
unique_ptr<T>
o tempo todo, exceto quando você precisar da contagem de referência, nesse caso, useshared_ptr<T>
(e para casos muito raros,weak_ptr<T>
para evitar ciclos de referência). Em quase todos os casos, a propriedade exclusiva transferível é ótima.Ponteiros brutos: bom apenas se você precisar de retornos covariantes, apontamentos não proprietários, o que pode acontecer. Caso contrário, não são terrivelmente úteis.
Ponteiros de matriz:
unique_ptr
possui uma especialização para aT[]
qual chama automaticamentedelete[]
o resultado, para que você possa fazê-lo com segurança,unique_ptr<int[]> p(new int[42]);
por exemplo.shared_ptr
você ainda precisaria de um deleter personalizado, mas não precisaria de um ponteiro de matriz exclusivo ou compartilhado especializado. Naturalmente, essas coisas geralmente são melhor substituídas destd::vector
qualquer maneira. Infelizmenteshared_ptr
, não fornece uma função de acesso à matriz; portanto, você ainda precisa chamar manualmenteget()
, masunique_ptr<T[]>
fornece emoperator[]
vez deoperator*
eoperator->
. Em qualquer caso, você deve verificar seus limites. Isso tornashared_ptr
um pouco menos fácil de usar, embora indiscutivelmente a vantagem genérica e nenhuma dependência do Boost façaunique_ptr
eshared_ptr
os vencedores novamente.Indicadores de escopo: tornados irrelevantes por
unique_ptr
, exatamente comoauto_ptr
.Não há realmente mais nada a ver. No C ++ 03 sem semântica de movimentação, essa situação era muito complicada, mas no C ++ 11 o conselho é muito simples.
Ainda existem usos para outros ponteiros inteligentes, como
intrusive_ptr
ouinterprocess_ptr
. No entanto, eles são muito nicho e completamente desnecessários no caso geral.fonte
std::unique_ptr<T[]>
fornece emoperator[]
vez deoperator*
eoperator->
. É verdade que você ainda precisa fazer uma verificação obrigatória.Casos de quando usar
unique_ptr
:Casos de quando usar
shared_ptr
:Casos de quando usar
weak_ptr
:Sinta-se livre para editar e adicionar mais
fonte