C++11 智能指針家族中,std::weak_ptr 是一種特殊的非擁有式觀察者。它不增加引用計數,不參與對象生命週期管理,專門用於解決 shared_ptr 循環引用導致的內存洩漏,以及實現緩存、觀察者模式等場景。
shared_ptr 管理的對象,構造/拷貝/析構不修改引用計數operator*、operator->,必須調用 lock() 獲取 shared_ptr 才能訪問對象lock() 成功時返回新 shared_ptr,延長對象壽命至該 shared_ptr 銷毀expired() 判斷對象是否已銷毀weak_ptr,讓引用計數歸零shared_ptr 子,子 weak_ptr 父shared_ptr 子,子節點 weak_ptr 父weak_ptr,對象銷毀自動失效weak_ptr,執行前 lock() 檢查對象存活| 函數 | 說明 |
|---|---|
lock() | 嘗試獲取共享所有權,成功返回 shared_ptr,失敗返回空 |
expired() | 等價於 use_count() == 0,檢查對象是否已銷毀 |
use_count() | 返回關聯 shared_ptr 的引用計數(僅供調試) |
reset() | 釋放觀察關係,置空 |
swap() | 交換兩個 weak_ptr |
std::weak_ptr<int> gw;
void f() {
if (auto spt = gw.lock()) {
std::cout << *spt << "\n";
} else {
std::cout << "gw is expired\n";
}
}
int test_weak_ptr1() {
{
auto sp = std::make_shared<int>(42);
gw = sp;
f(); // 輸出 42
} // sp 銷毀,對象釋放
f(); // 輸出 "gw is expired"
return 0;
}
int test_weak_ptr2() {
std::shared_ptr<int> sptr;
sptr.reset(new int);
*sptr = 10;
std::weak_ptr<int> weak1 = sptr;
sptr.reset(new int); // 原對象銷毀
*sptr = 5;
std::weak_ptr<int> weak2 = sptr;
if (auto tmp = weak1.lock()) std::cout << *tmp << '\n';
else std::cout << "weak1 is expired\n"; // 輸出 expired
if (auto tmp = weak2.lock()) std::cout << *tmp << '\n'; // 輸出 5
else std::cout << "weak2 is expired\n";
return 0;
}
class Controller {
public:
int Num;
std::string Status;
std::vector<std::weak_ptr<Controller>> others;
explicit Controller(int i) : Num(i), Status("On") {
std::cout << "Creating Controller" << Num << std::endl;
}
~Controller() { std::cout << "Destroying Controller" << Num << std::endl; }
void CheckStatuses() const {
for (auto& wp : others) {
if (auto p = wp.lock()) {
std::cout << "Status of " << p->Num << " = " << p->Status << std::endl;
} else {
std::cout << "Null object" << std::endl;
}
}
}
};
void RunTest() {
std::vector<std::shared_ptr<Controller>> v;
for (int i = 0; i < 5; ++i) v.push_back(std::make_shared<Controller>(i));
// 每個控制器觀察其他所有
for (int i = 0; i < v.size(); ++i) {
for (auto& p : v) {
if (p->Num != i) {
v[i]->others.push_back(p);
}
}
}
for (auto& p : v) {
std::cout << "use_count = " << p.use_count() << std::endl;
p->CheckStatuses();
}
}
struct Child;
struct Parent {
std::shared_ptr<Child> child;
~Parent() { std::cout << "Bye Parent" << std::endl; }
void hi() const { std::cout << "Hello" << std::endl; }
};
struct Child {
std::weak_ptr<Parent> parent; // 關鍵:用 weak_ptr
~Child() { std::cout << "Bye Child" << std::endl; }
};
int test_weak_ptr4() {
auto parent = std::make_shared<Parent>();
auto child = std::make_shared<Child>();
parent->child = child;
child->parent = parent;
child->parent.lock()->hi(); // 通過 lock 訪問父對象
return 0;
}
// 輸出:Hi → Bye Child → Bye Parent(正常銷毀)
class Node {
int value;
public:
std::shared_ptr<Node> leftPtr;
std::shared_ptr<Node> rightPtr;
std::weak_ptr<Node> parentPtr; // 父節點用 weak_ptr
Node(int val) : value(val) { std::cout << "Constructor\n"; }
~Node() { std::cout << "Destructor\n"; }
};
int test_weak_ptr5() {
auto ptr = std::make_shared<Node>(4);
ptr->leftPtr = std::make_shared<Node>(2);
ptr->leftPtr->parentPtr = ptr;
ptr->rightPtr = std::make_shared<Node>(5);
ptr->rightPtr->parentPtr = ptr;
std::cout << "ptr use_count = " << ptr.use_count() << std::endl; // 1
std::cout << "left use_count = " << ptr->leftPtr.use_count() << std::endl; // 1
std::cout << "right use_count = " << ptr->rightPtr.use_count() << std::endl; // 1
std::cout << "parentPtr lock count = " << ptr->rightPtr->parentPtr.lock().use_count() << std::endl; // 2
return 0;
}
weak_ptr 是 shared_ptr 的完美補充,解決「需訪問但不擁有」的場景lock(),再使用,lock() 返回的 shared_ptr 保證訪問期間對象存活use_count() 僅供調試,熱路徑避免頻繁調用GitHub 完整代碼:https://github.com/fengbingchun/Messy_Test
