Pergunta

C++ tem tudo a ver com propriedade de memória
Também conhecido 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 RAW é encapsulado, portanto, em um bom programa C++ (IMO), é muito raro [RARE not NUNCA] ver ponteiros RAW sendo transmitidos (já que ponteiros RAW não têm propriedade inferida, portanto, não podemos diga quem é o proprietário da memória e, portanto, sem uma leitura cuidadosa da documentação, você não poderá dizer quem é o responsável pela propriedade).

Por outro lado, é raro ver ponteiros RAW armazenados em uma classe. Cada ponteiro RAW é armazenado em seu próprio wrapper de ponteiro SMART.(Obs.: Se você não possui um objeto, você não deve armazená-lo porque 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 encontram?
  • 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 positivamente ou negativamente individualmente

Resumo:

Ponteiros conceitualmente inteligentes são simples e implementações ingênuas são fáceis.Já vi muitas tentativas de implementação, mas invariavelmente elas são quebradas de alguma forma que não é óbvia para uso e exemplos casuais.Portanto, recomendo sempre usar "Ponteiros Inteligentes" bem testados de uma biblioteca, em vez de criar os seus próprios.std::auto_ptr ou um dos ponteiros inteligentes de impulso parece cobrir todas as minhas necessidades.

std::auto_ptr<T>:

Uma ú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.

impulso::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 redefinido explicitamente.

impulso::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 chega a zero, o objeto é destruído.

Uso:
======
Quando o objeto pode ter múltiplas flores com um tempo de vida que não pode ser determinado em tempo de compilação.

impulso::fraco_ptr<T>

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

Uso:
======
Usado para impedir que os ciclos retenham objetos quando apenas o ciclo estiver mantendo uma refcount compartilhada.

Foi útil?

Solução

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 - igual ao anterior, mas é um 'escravo' de um shared_ptr, não é possível desalocar

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

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

Ainda passo objetos por referência (const sempre que possível), neste caso o método chamado deve assumir que o objeto está ativo apenas durante o momento da chamada.

Há outro tipo de ponteiro que eu uso e chamo 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_ptr para eles, mas não tem um shared_ptr para si mesmo.Como ele sabe que esses objetos não durariam mais que ele, ele passa um hub_ptr para eles (é apenas um wrapper de modelo para um ponteiro normal).

Outras dicas

Modelo C++ Simples

Na maioria dos módulos que vi, por padrão, presumia-se que o recebimento de ponteiros era não recebendo propriedade.Na verdade, funções/métodos que abandonavam 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 daquilo que aloca explicitamente.Todo o resto é descartado automaticamente (na saída do escopo ou por meio de RAII).Este é um modelo semelhante ao C, estendido pelo fato de que a maioria dos ponteiros pertence a objetos que os desalocarão automaticamente ou quando necessário (na destruição dos referidos objetos, principalmente), e que a duração da vida dos objetos é previsível (RAII é seu amigo, de novo).

Neste modelo, os ponteiros brutos circulam 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).

  • ponteiros brutos
  • std::auto_ptr
  • impulso::scoped_ptr

Modelo C++ de ponta inteligente

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ê terá que lidar com indicadores compartilhados e indicadores fracos.Portanto, você ainda precisa considerar a propriedade (o ponteiro fraco pode muito bem apontar para nada, mesmo que sua vantagem sobre o ponteiro bruto seja que ele pode lhe dizer isso).

  • impulso::shared_ptr
  • impulso::fraco_ptr

Conclusão

Não importa os modelos que eu descreva, salvo exceção, receber um ponteiro é não recebendo sua propriedade e ainda é muito importante saber quem é dono de quem.Mesmo para código C++ que usa muitas referências e/ou ponteiros inteligentes.

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

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

  • Propriedade Compartilhada
  • impulso::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 terminarem.

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

Do impulso, há também o contêiner de ponteiro biblioteca.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 ponteiros COM (IUnknown, IDispatch e amigos) e vários ponteiros inteligentes para lidar com eles (por exemplo,o ATL CComPtr e os ponteiros inteligentes gerados automaticamente pela instrução "import" no Visual Studio com base no _com_ptr aula).

  • Um proprietário
  • impulso::scoped_ptr

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

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

Acho que nunca estive em posição de compartilhar a propriedade do meu design.Na verdade, pensando bem, o único caso válido em que consigo pensar é o do padrão Flyweight.

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

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

Por que escrever outro ponteiro inteligente em C++?Já existem várias implementações de ponteiro inteligente de alta qualidade para C ++, com mais destaque para o Pointer Pantheon e o Smartptr de Loki.Para uma boa comparação de implementações de ponteiro inteligente e quando seu uso for apropriado, leia o novo C ++ da Herb Sutter:Ponteiros (mais) inteligentes.Em contraste com as características expansivas de outras bibliotecas, Yasper é um ponteiro de contagem de referência estreitamente focado.Ele corresponde intimamente com as políticas Shared_PTR do Boost e do Loki.Yasper permite que os programadores 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, já que Yasper permite ações de risco (mas úteis) (como atribuição a ponteiros crus e liberação manual) não permitidos por outras implementações.Tenha cuidado, use apenas esses recursos se você souber o que está fazendo!

Existe outra forma frequentemente usada de proprietário único transferível, e é preferível auto_ptr porque evita os problemas causados auto_ptrA corrupção insana da semântica de atribuição.

Falo de ninguém menos que swap.Qualquer tipo com um adequado swap função pode ser concebida como uma referência inteligente a algum conteúdo, de sua propriedade até o momento em que a propriedade seja transferida para outra instância do mesmo tipo, trocando-os.Cada instância manté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 desreferencia-la explicitamente para obter o conteúdo.)

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

  • Um proprietário:Também conhecido como excluir na cópia
  • std::auto_ptr

Quando o criador do objeto deseja entregar explicitamente a propriedade a outra pessoa.Essa também é uma forma de documentar o código que estou fornecendo a você e não estou mais monitorando-o, portanto, exclua-o quando terminar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top