Ответ
std::weak_ptr<T> — это «слабый» умный указатель, который не владеет объектом и не увеличивает счетчик ссылок (use_count) std::shared_ptr. Его основное назначение — разрушение циклических ссылок между shared_ptr и безопасное наблюдение за объектом, время жизни которого управляется shared_ptr.
Основные сценарии использования:
- Разрушение циклических зависимостей: Классический пример — двунаправленная связь или родитель-ребенок в графах.
- Кэширование: Хранение «слабых» ссылок на объекты, которые могут быть удалены, если на них больше нет «сильных» ссылок (
shared_ptr). - Наблюдение (observer): Временное получение доступа к ресурсу, если он еще жив, без продления его времени жизни.
Как с ним работать: weak_ptr нельзя разыменовать напрямую. Сначала нужно получить из него shared_ptr с помощью метода lock().
Пример с циклической зависимостью:
#include <iostream>
#include <memory>
class Node {
public:
std::string name;
std::shared_ptr<Node> partner; // Сильная ссылка
// std::weak_ptr<Node> partner; // Правильное решение: слабая ссылка
Node(const std::string& n) : name(n) {
std::cout << "Node " << name << " created.n";
}
~Node() {
std::cout << "Node " << name << " destroyed.n";
}
};
int main() {
std::cout << "=== Example with shared_ptr (Cyclic Reference) ===n";
{
auto alice = std::make_shared<Node>("Alice");
auto bob = std::make_shared<Node>("Bob");
alice->partner = bob; // bob.use_count() = 2
bob->partner = alice; // alice.use_count() = 2 -> ЦИКЛ!
// При выходе из области видимости деструкторы НЕ вызовутся из-за цикла.
}
std::cout << "Scope ended. Memory leak!nn";
std::cout << "=== Example with weak_ptr (Correct) ===n";
{
auto alice = std::make_shared<Node>("Alice");
auto bob = std::make_shared<Node>("Bob");
// Используем weak_ptr для разрыва цикла владения
std::weak_ptr<Node> aliceWeak = alice;
std::weak_ptr<Node> bobWeak = bob;
// Для доступа к партнеру используем lock()
if (auto partnerPtr = bobWeak.lock()) { // Пытаемся получить shared_ptr
std::cout << "Bob's partner (via weak_ptr) is: " << partnerPtr->name << std::endl;
} else {
std::cout << "Bob's partner no longer exists.n";
}
// При выходе из области видимости alice и bob будут корректно уничтожены,
// т.к. weak_ptr не мешает счетчику ссылок достичь нуля.
}
std::cout << "Scope ended. No memory leak.n";
return 0;
}
lock() возвращает shared_ptr на объект, если он еще существует (счетчик use_count > 0), или nullptr в противном случае. Это потокобезопасная операция.