use - weak_ptr example c++




Diferença em shared_ptr make_shared e normal em C++ (5)

A diferença é que std::make_shared executa uma alocação de heap, enquanto que chamar o construtor std::shared_ptr executa dois.

Onde as alocações de heap acontecem?

std::shared_ptr gerencia duas entidades:

  • o bloco de controle (armazena metadados como ref-counts, deleter tipo apagado, etc)
  • o objeto sendo gerenciado

std::make_shared executa uma única contabilidade de alocação de heap para o espaço necessário para o bloco de controle e os dados. No outro caso, new Obj("foo") chama uma alocação de heap para os dados gerenciados e o construtor std::shared_ptr executa outro para o bloco de controle.

Para mais informações, confira as notas de implementação na cppreference .

Atualização I: Exceção-Segurança

Como o OP parece estar se perguntando sobre o lado da exceção de segurança, atualizei minha resposta.

Considere este exemplo,

void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }

F(std::shared_ptr<Lhs>(new Lhs("foo")),
  std::shared_ptr<Rhs>(new Rhs("bar")));

Como o C ++ permite uma ordem arbitrária de avaliação de subexpressões, uma possível ordenação é:

  1. new Lhs("foo"))
  2. new Rhs("bar"))
  3. std::shared_ptr<Lhs>
  4. std::shared_ptr<Rhs>

Agora, suponha que tenhamos uma exceção lançada na etapa 2 (por exemplo, exceção out of memory, o construtor Rhs emitiu algumas exceções). Em seguida, perdemos a memória alocada na etapa 1, pois nada terá a chance de limpá-la. O núcleo do problema aqui é que o ponteiro bruto não foi passado para o construtor std::shared_ptr imediatamente.

Uma maneira de corrigir isso é executá-las em linhas separadas, de modo que essa ordenação arbitrária não possa ocorrer.

auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);

A maneira preferida de resolver isso é usar std::make_shared .

F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));

Atualização II: Desvantagem de std::make_shared

Citando os comentários de Casey :

Como há apenas uma alocação, a memória do pointee não pode ser desalocada até que o bloco de controle não esteja mais em uso. Um weak_ptr pode manter o bloco de controle ativo indefinidamente.

Por que instâncias de weak_ptr s mantêm o bloco de controle ativo?

Deve haver uma maneira de o weak_ptr s determinar se o objeto gerenciado ainda é válido (por exemplo, para lock ). Eles fazem isso verificando o número de shared_ptr s que possuem o objeto gerenciado, que é armazenado no bloco de controle. O resultado é que os blocos de controle estão weak_ptr até a contagem shared_ptr e a contagem weak_ptr atingirem 0.

Voltar para std::make_shared

Como std::make_shared faz uma única alocação de heap para o bloco de controle e o objeto gerenciado, não há como liberar a memória para o bloco de controle e o objeto gerenciado independentemente. Temos que esperar até podermos liberar tanto o bloco de controle quanto o objeto gerenciado, o que acontece até que não haja shared_ptr s ou weak_ptr s vivos.

Suponha que, em vez disso, tenhamos executado duas alocações de heap para o bloco de controle e o objeto gerenciado por meio do construtor new e shared_ptr . Então nós liberamos a memória para o objeto gerenciado (talvez antes) quando não há shared_ptr vivos, e weak_ptr a memória para o bloco de controle (talvez mais tarde) quando não houver weak_ptr ativo.

https://code.i-harness.com

std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));

Muitas postagens do google e stackoverflow estão lá, mas não consigo entender por que o make_shared é mais eficiente do que usar diretamente o shared_ptr .

Alguém pode me explicar passo a passo a seqüência de objetos criados e operações feitas por ambos para que eu seja capaz de entender como o make_shared é eficiente. Eu dei um exemplo acima para referência.


Eu vejo um problema com std :: make_shared, ele não suporta construtores privados / protegidos


O ponteiro compartilhado gerencia o próprio objeto e um pequeno objeto que contém a contagem de referência e outros dados de manutenção. make_shared pode alocar um único bloco de memória para armazenar os dois; A construção de um ponteiro compartilhado de um ponteiro para um objeto já alocado precisará alocar um segundo bloco para armazenar a contagem de referência.

Além dessa eficiência, o uso de make_shared significa que você não precisa lidar com ponteiros new e crus, oferecendo melhor segurança de exceção - não há possibilidade de lançar uma exceção após alocar o objeto, mas antes de atribuí-lo ao ponteiro inteligente .


Se você precisar de alinhamento especial de memória no objeto controlado por shared_ptr, não poderá confiar no make_shared, mas acho que é o único bom motivo para não usá-lo.


Shared_ptr : Executa duas alocações de heap

  1. Bloco de controle (contagem de referência)
  2. Objeto sendo gerenciado

Make_shared : Executa apenas uma alocação de heap

  1. Bloco de controle e dados do objeto.




shared-ptr