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 é:
-
new Lhs("foo"))
-
new Rhs("bar"))
-
std::shared_ptr<Lhs>
-
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
- Bloco de controle (contagem de referência)
- Objeto sendo gerenciado
Make_shared
: Executa apenas uma alocação de heap
- Bloco de controle e dados do objeto.