c++ - Ponteiros Inteligentes: Ou quem é seu dono, baby?




memory-management smart-pointers (8)

Modelo simples de C ++

Na maioria dos módulos vi, por padrão, supunha-se que receber ponteiros não estava recebendo propriedade. De fato, funções / métodos abandonando a propriedade de um ponteiro eram ambos muito raros e explicitamente expressavam esse fato em sua documentação.

Este modelo assume que o usuário é proprietário apenas do que ele / ela aloca explicitamente . Todo o resto é automaticamente eliminado (na saída do osciloscópio ou através do RAII). Este é um modelo tipo C, estendido pelo fato de a maioria dos ponteiros pertencerem a objetos que irão desalocá-los automaticamente ou quando necessário (na destruição de objetos, principalmente) e que a duração de vida dos objetos é previsível (RAII é seu amigo, novamente).

Nesse modelo, os ponteiros brutos circulam livremente e, na maioria das vezes, não são perigosos (mas se o desenvolvedor for inteligente o suficiente, ele usará referências sempre que possível).

  • ponteiros crus
  • std :: auto_ptr
  • boost :: scoped_ptr

Modelo C ++ Apontado Inteligente

Em um código cheio de ponteiros inteligentes, o usuário pode esperar ignorar a vida útil dos objetos. O dono nunca é o código do usuário: é o próprio ponteiro inteligente (RAII, novamente). O problema é que referências circulares misturadas com referências inteligentes contadas podem ser mortais , então você tem que lidar tanto com ponteiros compartilhados quanto com ponteiros fracos. Então você ainda tem que ser considerado (o ponteiro fraco poderia apontar para nada, mesmo que sua vantagem sobre o ponteiro bruto seja que ele possa lhe dizer isso).

  • boost :: shared_ptr
  • boost :: weak_ptr

Conclusão

Não importa os modelos que eu descrevo, a menos que a exceção, recebendo um ponteiro não está recebendo a sua propriedade e ainda é muito importante saber quem é dono de quem . Mesmo para o código C ++ usando fortemente referências e / ou ponteiros inteligentes.

C ++ é tudo sobre a propriedade de memória
Aka " Semântica da Propriedade "

É de responsabilidade do proprietário de uma parte da memória alocada dinamicamente liberar essa memória. Então a questão realmente se torna quem possui a memória.

Em C ++ a propriedade é documentada pelo tipo que um ponteiro RAW é empacotado internamente em um bom programa C ++ (IMO) é muito raro [RARA NUNCA] ver ponteiros RAW passados ​​(como ponteiros RAW não possuem propriedade inferida portanto não podemos diga quem é o proprietário da memória e, portanto, sem ler atentamente a documentação, você não sabe quem é responsável pela propriedade).

Por outro lado, é raro ver ponteiros RAW armazenados em uma classe em que cada ponteiro RAW é armazenado em seu próprio wrapper de ponteiro SMART. ( NB: Se você não possui um objeto, você não deve armazená-lo porque você não pode saber quando ele sairá do escopo e será destruído.)

Então a questão:

  • Que tipo de propriedade semântica as pessoas têm?
  • Quais classes padrão são usadas para implementar essas semânticas?
  • Que situações você acha útil?

Vamos manter 1 tipo de propriedade semântica por resposta para que possam ser votados para cima e para baixo individualmente

Resumo:

Os indicadores conceitualmente inteligentes são simples e as implementações ingênuas são fáceis. Eu tenho visto muitas tentativas de implementação, mas invariavelmente elas são quebradas de alguma forma que não é óbvia para uso casual e exemplos. Assim, recomendo sempre usar "ponteiros inteligentes" bem testados em uma biblioteca, em vez de usar os seus próprios. std :: auto_ptr ou um dos ponteiros inteligentes de impulso parecem cobrir todas as minhas necessidades.

std :: auto_ptr <T>:

Única pessoa possui o objeto.
Mas a transferência de propriedade é permitida.

Uso:
======
Isso permite definir interfaces que mostram a transferência explícita de propriedade.

boost :: scoped_ptr <T>

Única pessoa possui o objeto.
A transferência de propriedade NÃO é permitida.

Uso:
======
Usado para mostrar propriedade explícita.
Objeto será destruído pelo destruidor ou quando explicitamente redefinido.

boost :: shared_ptr <T> (std :: tr1 :: shared_ptr <T>)

Propriedade múltipla
Este é um ponteiro contado de referência simples. Quando a contagem de referência atinge zero, o objeto é destruído.

Uso:
======
Quando o objeto pode ter vários recursos com uma vida útil que não pode ser determinada em tempo de compilação.

boost :: weak_ptr <T>

Usado com shared_ptr <T>.
Em situações em que um ciclo de ponteiros pode acontecer.

Uso:
======
Usado para impedir que os ciclos retenham objetos quando apenas o ciclo está mantendo uma referência compartilhada.


Do boost, há também a biblioteca de contêineres de ponteiros . Eles são um pouco mais eficientes e fáceis de usar do que um contêiner padrão de ponteiros inteligentes, se você só estiver usando os objetos no contexto de seu contêiner.

No Windows, há os ponteiros COM (IUnknown, IDispatch e amigos) e vários ponteiros inteligentes para manipulá-los (por exemplo, o CComPtr da ATL e os ponteiros inteligentes gerados automaticamente pela instrução "import" no Visual Studio com base na classe _com_ptr ).


Não acho que tenha estado em posição de compartilhar a propriedade do meu design. Na verdade, do topo da minha cabeça, o único caso válido em que posso pensar é o padrão Flyweight.


Não compartilhe a propriedade. Se fizer isso, verifique se é apenas com o código que você não controla.

Isso resolve 100% dos problemas, pois força você a entender como tudo interage.


yasper :: ptr é um peso leve, boost :: shared_ptr como alternativa. Funciona bem no meu (por enquanto) projeto pequeno.

Na página da web em http://yasper.sourceforge.net/ ele é descrito da seguinte maneira:

Por que escrever outro ponteiro inteligente C ++? Já existem várias implementações de ponteiro inteligente de alta qualidade para C ++, mais proeminentemente o panteão de ponteiro Boost e o SmartPtr de Loki. Para uma boa comparação de implementações de ponteiros inteligentes e quando o seu uso for apropriado, por favor leia The New C ++: Smart (er) Pointers, de Herb Sutter. Em contraste com os recursos expansivos de outras bibliotecas, o Yasper é um ponteiro de contagem de referência com foco estreito. Ele corresponde de perto às políticas shared_ptr do Boost e RefCounted / AllowConversion do Loki. O Yasper permite que os programadores de C ++ esqueçam o gerenciamento de memória sem introduzir as grandes dependências do Boost ou ter que aprender sobre os complicados modelos de política do Loki. Filosofia

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

O último ponto pode ser perigoso, uma vez que o yasper permite ações arriscadas (ainda que úteis) (como atribuição a ponteiros brutos e liberação manual) não permitidas por outras implementações. Tenha cuidado, use apenas esses recursos se você sabe o que está fazendo!


std::tr1::shared_ptr<Blah> é muitas vezes a sua melhor aposta.


  • Um proprietário
  • boost :: scoped_ptr

Quando você precisa alocar memória dinamicamente, mas quer ter certeza de que ela é desalocada em todos os pontos de saída do bloco.

Acho isso útil, pois pode ser facilmente recolocado e liberado sem ter que se preocupar com um vazamento


  • Um proprietário: Aka delete on Copy
  • std :: auto_ptr

Quando o criador do objeto deseja entregar explicitamente a propriedade a outra pessoa. Esta é também uma maneira de documentar no código que estou dando isso para você e eu não estou mais acompanhando isso, então certifique-se de apagá-lo quando terminar.





ownership-semantics