Desafio C++
Evitando ciclo com weak_ptr
Enunciado
Modele uma relação pai-filho onde o pai tem um shared_ptr para o filho e o filho tem um weak_ptr para o pai. Crie as classes Pai e Filho, instancie e verifique que não há vazamento (o destrutor de ambos é chamado).
Requisitos
Paicontémstd::shared_ptr<Filho>.Filhocontémstd::weak_ptr<Pai>.- Imprimir mensagens nos destrutores para confirmar que ambos são destruídos.
- Usar
lock()para acessar o pai a partir do filho.
Código inicial
#include <iostream>
#include <memory>
// Defina as classes Pai e Filho aqui
int main() {
// seu código aqui
return 0;
}
Saída esperada
Pai criado
Filho criado
Acessando pai via weak_ptr: pai
~Pai
~Filho
Ver dica
O weak_ptr não impede a destruição. Use lock() para obter um shared_ptr temporário e acessar o objeto.
Mostrar solução
#include <iostream>
#include <memory>
#include <string>
struct Filho; // forward declaration
struct Pai {
std::string nome;
std::shared_ptr<Filho> filho;
Pai(std::string n) : nome(std::move(n)) {
std::cout << "Pai criado\n";
}
~Pai() { std::cout << "~Pai\n"; }
};
struct Filho {
std::string nome;
std::weak_ptr<Pai> pai; // weak para evitar ciclo
Filho(std::string n) : nome(std::move(n)) {
std::cout << "Filho criado\n";
}
~Filho() { std::cout << "~Filho\n"; }
};
int main() {
auto p = std::make_shared<Pai>("pai");
auto f = std::make_shared<Filho>("filho");
p->filho = f; // pai possui filho
f->pai = p; // filho observa pai (weak)
// Acessa o pai a partir do filho
if (auto locked = f->pai.lock()) {
std::cout << "Acessando pai via weak_ptr: " << locked->nome << '\n';
}
return 0;
}
Passo a passo
- Declaramos
struct Filho;antes dePaiporquePaicontém umshared_ptr<Filho>. Paitem umshared_ptr<Filho>e um destrutor que imprime~Pai.Filhotem umweak_ptr<Pai>e um destrutor que imprime~Filho.- Em
main, criamospefcommake_shared. O contador depé 1, o defé 1. p->filho = f;incrementa o contador defpara 2.f->pai = p;não altera o contador dep(weak).f->pai.lock()obtém umshared_ptrtemporário parape acessamosnome.- Ao final de
main,fepsão destruídos. O contador defcai para 0 (destruído), depoispé destruído. SeFilhotivesseshared_ptr<Pai>, haveria ciclo e nenhum seria destruído.
Por que funciona
O weak_ptr não participa da contagem de referências, então não mantém o objeto vivo. Isso quebra o ciclo de posse que ocorreria se ambos usassem shared_ptr. O lock() cria um shared_ptr temporário apenas durante o uso, garantindo que o objeto exista enquanto acessado.
Erros comuns
- Usar
shared_ptrno filho:std::shared_ptr<Pai> pai;cria um ciclo e vaza memória. - Esquecer de verificar
lock(): se o pai já foi destruído,lock()retornanullptr; desreferenciar causa erro. - Tentar acessar
f->pai->nomediretamente:weak_ptrnão tem operador->; é precisolock().
Outra forma de resolver
Poderia usar std::enable_shared_from_this para obter shared_ptr a partir de this, mas isso não resolve o ciclo; ainda seria necessário weak_ptr em um dos lados.
Saída esperada
Pai criado
Filho criado
Acessando pai via weak_ptr: pai
~Pai
~Filho