C++智能指针weak_ptr
weak_ptr
是 C++11 引入的智能指针之一,通常与 shared_ptr
配合使用,用于解决 shared_ptr
可能出现的循环引用问题。
一、什么是 weak_ptr
weak_ptr
是一种“弱引用”智能指针,它不会增加所管理对象的引用计数。它的主要作用是用来观察或访问由 shared_ptr
管理的资源,而不拥有该资源的所有权。由于不拥有所有权,weak_ptr
指向的对象可能在某些情况下已经被销毁,因此在使用时需要特别小心。
二、基本特点
- 不控制生命周期:
weak_ptr
不会影响对象的引用计数,因此不会阻止对象被销毁。 - 依赖
shared_ptr
:weak_ptr
必须从一个shared_ptr
或另一个weak_ptr
创建。 - 解决循环引用:当两个对象通过
shared_ptr
互相引用时,会导致内存泄漏。使用weak_ptr
可以打破这种循环。 - 需要检查有效性:由于
weak_ptr
指向的对象可能已经被销毁,使用前需要通过lock()
方法转换为shared_ptr
并检查是否有效。
三、基本用法
以下是 weak_ptr
的常见用法及其代码示例:
1. 创建 weak_ptr
weak_ptr
通常从 shared_ptr
初始化:
#include <iostream>
#include <memory>int main() {std::shared_ptr<int> sp = std::make_shared<int>(42);std::weak_ptr<int> wp = sp; // 从 shared_ptr 创建 weak_ptrstd::cout << "shared_ptr use_count: " << sp.use_count() << std::endl; // 输出 1return 0;
}
wp
是从sp
创建的弱引用。sp.use_count()
返回 1,因为weak_ptr
不增加引用计数。
2. 使用 lock()
获取 shared_ptr
在使用 weak_ptr
访问对象之前,必须通过 lock()
将其转换为 shared_ptr
,并检查是否有效:
#include <iostream>
#include <memory>int main() {std::shared_ptr<int> sp = std::make_shared<int>(42);std::weak_ptr<int> wp = sp;if (auto locked = wp.lock()) { // 尝试获取 shared_ptrstd::cout << "Value: " << *locked << std::endl; // 输出 42std::cout << "use_count: " << locked.use_count() << std::endl; // 输出 2} else {std::cout << "Object has been destroyed" << std::endl;}sp.reset(); // 释放 shared_ptrif (auto locked = wp.lock()) {std::cout << "Value: " << *locked << std::endl;} else {std::cout << "Object has been destroyed" << std::endl; // 输出此行}return 0;
}
lock()
返回一个shared_ptr
,如果对象仍存在则有效,否则返回空指针。- 当
sp
被销毁后,wp.lock()
返回空指针,表示资源已不可用。
3. 检查是否过期
可以用 expired()
方法检查 weak_ptr
是否指向已销毁的对象:
#include <iostream>
#include <memory>int main() {std::weak_ptr<int> wp;{std::shared_ptr<int> sp = std::make_shared<int>(42);wp = sp;std::cout << "Expired? " << wp.expired() << std::endl; // 输出 0 (false)}std::cout << "Expired? " << wp.expired() << std::endl; // 输出 1 (true)return 0;
}
expired()
返回true
表示对象已销毁,false
表示仍有效。
四、解决循环引用问题
weak_ptr
的核心应用场景是打破 shared_ptr
的循环引用。以下是一个经典例子:
循环引用问题
#include <iostream>
#include <memory>struct Node {std::shared_ptr<Node> next;~Node() { std::cout << "Node destroyed" << std::endl; }
};int main() {auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2;node2->next = node1; // 循环引用std::cout << "End of main" << std::endl;return 0;
}
- 输出只有 “End of main”,
Node
的析构函数未被调用,因为node1
和node2
互相持有对方的shared_ptr
,引用计数无法降到 0。
使用 weak_ptr
解决问题
#include <memory>
#include <iostream>struct Node{std::weak_ptr<Node> next;~Node(){std::cout << "Node deleted" << std::endl;}
};int main() {std::weak_ptr<Node> wp1,wp2;{auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2;node2->next = node1;wp1 = node1;wp2 = node2;std::cout << node1.use_count() <<"," << node2.use_count() << std::endl;}std::cout << wp1.use_count() <<"," << wp2.use_count() << std::endl;std::cout << "End of main " << std::endl;return 0;
}
- 输出:
1,1Node deletedNode deleted0,0End of main
- 使用
weak_ptr
后,循环引用被打破,对象能正常销毁。
五、注意事项
- 不能直接解引用:
weak_ptr
没有operator*
或operator->
,必须通过lock()
转换为shared_ptr
。 - 线程安全:
weak_ptr
的lock()
操作是线程安全的,但对其本身的赋值或构造不是。 - 性能开销:
weak_ptr
的使用会引入少量开销(如检查有效性),但在需要避免循环引用时是值得的。
六、总结
- 用途:
weak_ptr
主要用于观察资源或打破shared_ptr
的循环引用。 - 关键方法:
lock()
获取shared_ptr
,expired()
检查有效性。 - 典型场景:对象间存在潜在循环引用(如链表、树结构)时。