Dicas inteligentes: de quem é o objeto? [fechadas]

114

C ++ é tudo sobre propriedade de memória - também conhecida como semântica de propriedade .

É responsabilidade do proprietário de um pedaço de memória alocada dinamicamente liberar essa memória. Portanto, a questão realmente é quem é o dono da memória.

Em C ++, a propriedade é documentada pelo tipo em que um ponteiro bruto é envolvido, portanto, em um bom programa C ++ (IMO), é muito raro ( raro , não nunca ) ver ponteiros não processados ​​passados ​​(pois os ponteiros não têm propriedade inferida, portanto, podemos não dizer a quem pertence a memória e, portanto, sem uma leitura cuidadosa da documentação, você não pode dizer quem é o responsável pela propriedade)

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

Então a pergunta:

  • Que tipo de semântica de propriedade as pessoas encontraram?
  • Quais classes padrão são usadas para implementar essa semântica?
  • Em que situações você os considera úteis?

Vamos manter 1 tipo de propriedade semântica por resposta para que possam ser votadas individualmente.

Resumo:

Conceitualmente, os ponteiros inteligentes são simples e uma implementação ingênua é fácil. Tenho visto muitas tentativas de implementação, mas invariavelmente elas são interrompidas de alguma forma que não é óbvia para o uso casual e exemplos. Portanto, eu recomendo sempre usar ponteiros inteligentes bem testados de uma biblioteca ao invés de rolar seus próprios. std::auto_ptrou uma das dicas inteligentes Boost parece cobrir todas as minhas necessidades.

std::auto_ptr<T>:

Uma única pessoa possui o objeto. A transferência de propriedade é permitida.

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

boost::scoped_ptr<T>

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

Uso: usado para mostrar propriedade explícita. O 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 de contagem de referência simples. Quando a contagem de referência chega a zero, o objeto é destruído.

Uso: Quando um objeto pode ter várias flores 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 pode ocorrer um ciclo de ponteiros.

Uso: Usado para impedir que os ciclos retenham objetos quando apenas o ciclo está mantendo um refcount compartilhado.

Martin York
fonte

Respostas:

20

Para mim, esses 3 tipos cobrem a maioria das minhas necessidades:

shared_ptr - contagem de referência, desalocação quando o contador chega a zero

weak_ptr- o mesmo que acima, mas é um 'escravo' para a shared_ptr, não pode desalocar

auto_ptr- quando a criação e desalocação acontecem dentro da mesma função, ou quando o objeto tem que ser considerado sempre como único proprietário. Quando você atribui um ponteiro a outro, o segundo 'rouba' o objeto do primeiro.

Tenho minha própria implementação para eles, mas também estão disponíveis em Boost.

Eu ainda passo objetos por referência ( constsempre que possível), neste caso o método chamado deve assumir que o objeto está vivo apenas durante o tempo da chamada.

Há outro tipo de ponteiro que uso e que chamo de hub_ptr . É quando você tem um objeto que deve ser acessível a partir de objetos aninhados nele (geralmente como uma classe base virtual). Isso poderia ser resolvido passando um weak_ptrpara eles, mas não tem um shared_ptrpara si mesmo. Como sabe que esses objetos não viveriam mais do que ele, passa um hub_ptr para eles (é apenas um wrapper de template para um ponteiro regular).

Fabio Ceconello
fonte
23

Modelo C ++ Simples

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

Este modelo assume que o usuário é proprietário apenas do que ele aloca explicitamente . Todo o resto é descartado automaticamente (na saída do osciloscópio ou por meio do RAII). Este é um modelo semelhante ao 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 tais objetos, principalmente), e que a duração da vida dos objetos é previsível (RAII é seu amigo, novamente).

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

  • dicas cruas
  • std :: auto_ptr
  • boost :: scoped_ptr

Modelo C ++ Smart Pointed

Em um código cheio de ponteiros inteligentes, o usuário pode esperar ignorar o tempo de vida dos objetos. O proprietário nunca é o código do usuário: é o próprio ponteiro inteligente (RAII, novamente). O problema é que referências circulares misturadas com ponteiros inteligentes contados por referência podem ser mortais , então você tem que lidar com ponteiros compartilhados e ponteiros fracos. Portanto, você ainda deve considerar a propriedade (o ponteiro fraco pode muito bem apontar para nada, mesmo que sua vantagem sobre o ponteiro bruto seja que ele pode informá-lo).

  • boost :: shared_ptr
  • boost :: weak_ptr

Conclusão

Não importa os modelos que descrevo, salvo exceção, receber um ponteiro não é receber sua propriedade e ainda é muito importante saber quem possui quem . Mesmo para código C ++, usando fortemente referências e / ou ponteiros inteligentes.

paercebal
fonte
10

Não tem propriedade compartilhada. Se você fizer isso, certifique-se de que seja apenas com código que você não controla.

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

MSN
fonte
2
  • Propriedade Compartilhada
  • boost :: shared_ptr

Quando um recurso é compartilhado entre vários objetos. O boost shared_ptr usa contagem de referência para garantir que o recurso seja desalocado quando todos estiverem terminados.

Martin York
fonte
2

std::tr1::shared_ptr<Blah> frequentemente é sua melhor aposta.

Matt Cruikshank
fonte
2

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

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

Ryan Ginstrom
fonte
1
  • Um proprietário
  • boost :: scoped_ptr

Quando você precisa alocar memória dinamicamente, mas deseja ter certeza de que ela será desalocada em cada ponto de saída do bloco.

Acho isso útil porque pode ser facilmente reinstalado e liberado sem nunca ter que se preocupar com um vazamento

Pieter
fonte
1

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

Nemanja Trifunovic
fonte
1

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

Na página da web em http://yasper.sourceforge.net/ é 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 ++, principalmente o panteão de ponteiro Boost e o SmartPtr de Loki. Para uma boa comparação de implementações de ponteiros inteligentes e quando seu uso é apropriado, leia The New C ++: Smart (er) Pointers de Herb Sutter. Em contraste com os recursos expansivos de outras bibliotecas, Yasper é um ponteiro de contagem de referência estreitamente focado. Corresponde intimamente às políticas shared_ptr de Boost e RefCounted / AllowConversion de Loki. O Yasper permite que os programadores C ++ se esqueçam do 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 (mas úteis) (como atribuição a ponteiros brutos e liberação manual) não permitidas por outras implementações. Cuidado, só use esses recursos se souber o que está fazendo!

Hernán
fonte
1

Há outra forma freqüentemente usada de proprietário único transferível, e é preferível auto_ptrporque evita os problemas causados ​​pela auto_ptrcorrupção insana da semântica de atribuição.

Não falo de outro senão swap. Qualquer tipo com uma swapfunção adequada pode ser concebido como uma referência inteligente a algum conteúdo, o qual possui até o momento em que a propriedade é transferida para outra instância do mesmo tipo, trocando-os. Cada instância retém sua identidade, mas fica vinculada a um novo conteúdo. É como uma referência religável com segurança.

(É uma referência inteligente, em vez de um ponteiro inteligente, porque você não precisa desreferenciá-la explicitamente para obter o conteúdo.)

Isso significa que auto_ptr se torna menos necessário - ele só é necessário para preencher as lacunas onde os tipos não têm uma boa swapfunção. Mas todos os contêineres std têm.

Daniel Earwicker
fonte
0
  • Um proprietário: Também conhecido como exclusão na cópia
  • std :: auto_ptr

Quando o criador do objeto deseja passar explicitamente a propriedade para outra pessoa. Essa também é uma forma de documentar no código que estou fornecendo e não estou mais rastreando, portanto, certifique-se de excluí-lo quando terminar.

Martin York
fonte