C++ - 简单实现std::weak_ptr
我们无法直接模拟std::weak_ptr,因为它的功能依赖于std::shared_ptr的控制块。但是,我们可以通过扩展之前实现的MySharedPtr来模拟weak_ptr的基本行为。
思路:
-
在MySharedPtr的控制块中增加一个弱引用计数。
-
MyWeakPtr将包含与控制块相同的指针,但不会增加强引用计数,只会增加弱引用计数。
-
当强引用计数为0时,删除托管对象,但控制块要等到弱引用计数也为0时才删除。
-
MyWeakPtr可以通过lock()方法获得一个MySharedPtr,如果对象还存在则增加强引用计数,否则返回一个空的MySharedPtr。
我们先修改MySharedPtr,增加对弱引用的支持,然后实现MyWeakPtr。
步骤:
-
修改控制块,包含强引用计数和弱引用计数。
-
修改MySharedPtr,使其支持弱引用。
-
实现MyWeakPtr。
注意:由于我们之前实现的MySharedPtr是使用单独的int*作为引用计数,现在我们需要一个结构体来同时保存强引用和弱引用计数。
控制块结构体:
template<typename T>
struct ControlBlock {
T* object; // 托管对象的指针
int strong_count; // 强引用计数
int weak_count; // 弱引用计数
};
然后,我们修改MySharedPtr,使其使用ControlBlock。
由于时间关系,我们只实现最关键的部分。
注意:当强引用计数为0时,删除object,但不删除ControlBlock,直到弱引用计数也为0才删除ControlBlock。
我们先重新实现MySharedPtr,然后实现MyWeakPtr。
太好了!手动实现weak_ptr能让我们深入理解它如何与shared_ptr协同工作来解决循环引用问题。我会基于之前实现的MySharedPtr来构建MyWeakPtr。
🎯 目标:理解弱引用机制
我们要实现以下核心功能:
-
不增加引用计数
-
通过
lock()获取可用的shared_ptr -
检测所指向对象是否已被销毁
-
与
shared_ptr的控制块协同工作
💻 完整代码实现
首先,我们需要修改之前的MySharedPtr来支持控制块:
#include <iostream>
#include <utility>
// 控制块结构
template<typename T>
struct ControlBlock {
T* object; // 实际对象指针
int strong_count; // 强引用计数
int weak_count; // 弱引用计数
ControlBlock(T* ptr) : object(ptr), strong_count(1), weak_count(0) {}
~ControlBlock() {
std::cout << "控制块被销毁" << std::endl;
}
};
// 前置声明
template<typename T>
class MyWeakPtr;
template<typename T>
class MySharedPtr {
private:
T* m_ptr; // 对象指针
ControlBlock<T>* m_control; // 控制块指针
public:
// 让MyWeakPtr可以访问私有成员
friend class MyWeakPtr<T>;
// 构造函数
explicit MySharedPtr(T* ptr = nullptr)
: m_ptr(ptr), m_control(ptr ? new ControlBlock<T>(ptr) : nullptr) {
std::cout << "MySharedPtr 构造,强引用: "
<< (m_control ? m_control->strong_count : 0) << std::endl;
}
// 拷贝构造
MySharedPtr(const MySharedPtr& other)
: m_ptr(other.m_ptr), m_control(other.m_control) {
if (m_control) {
++m_control->strong_count;
std::cout << "MySharedPtr 拷贝构造,强引用: "
<< m_control->strong_count << std::endl;
}
}
// 移动构造
MySharedPtr(MySharedPtr&& other) noexcept
: m_ptr(other.m_ptr), m_control(other.m_control) {
other.m_ptr = nullptr;
other.m_control = nullptr;
std::cout << "MySharedPtr 移动构造" << std::endl;
}
// 析构函数
~MySharedPtr() {
cleanup();
}
// 解引用操作符
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
T* get() const { return m_ptr; }
int use_count() const {
return m_control ? m_control->strong_count : 0;
}
private:
void cleanup() {
if (m_control) {
--m_control->strong_count;
std::cout << "MySharedPtr 清理,强引用: "
<< m_control->strong_count << std::endl;
if (m_control->strong_count == 0) {
std::cout << "删除托管对象" << std::endl;
delete m_ptr;
m_ptr = nullptr;
// 如果没有弱引用了,也删除控制块
if (m_control->weak_count == 0) {
delete m_control;
}
}
m_control = nullptr;
}
}
};
现在实现MyWeakPtr:
template<typename T>
class MyWeakPtr {
private:
T* m_ptr; // 对象指针(可能已失效)
ControlBlock<T>* m_control; // 控制块指针
public:
// 1. 默认构造函数
MyWeakPtr() noexcept : m_ptr(nullptr), m_control(nullptr) {}
// 2. 从shared_ptr构造
MyWeakPtr(const MySharedPtr<T>& shared) noexcept
: m_ptr(shared.m_ptr), m_control(shared.m_control) {
if (m_control) {
++m_control->weak_count;
std::cout << "MyWeakPtr 从shared_ptr构造,弱引用: "
<< m_control->weak_count << std::endl;
}
}
// 3. 拷贝构造函数
MyWeakPtr(const MyWeakPtr& other) noexcept
: m_ptr(other.m_ptr), m_control(other.m_control) {
if (m_control) {
++m_control->weak_count;
std::cout << "MyWeakPtr 拷贝构造,弱引用: "
<< m_control->weak_count << std::endl;
}
}
// 4. 移动构造函数
MyWeakPtr(MyWeakPtr&& other) noexcept
: m_ptr(other.m_ptr), m_control(other.m_control) {
other.m_ptr = nullptr;
other.m_control = nullptr;
std::cout << "MyWeakPtr 移动构造" << std::endl;
}
// 5. 析构函数
~MyWeakPtr() {
cleanup();
}
// 6. 拷贝赋值运算符
MyWeakPtr& operator=(const MyWeakPtr& other) noexcept {
if (this != &other) {
cleanup();
m_ptr = other.m_ptr;
m_control = other.m_control;
if (m_control) {
++m_control->weak_count;
std::cout << "MyWeakPtr 拷贝赋值,弱引用: "
<< m_control->weak_count << std::endl;
}
}
return *this;
}
// 7. 从shared_ptr赋值
MyWeakPtr& operator=(const MySharedPtr<T>& shared) noexcept {
cleanup();
m_ptr = shared.m_ptr;
m_control = shared.m_control;
if (m_control) {
++m_control->weak_count;
std::cout << "MyWeakPtr 从shared_ptr赋值,弱引用: "
<< m_control->weak_count << std::endl;
}
return *this;
}
// 8. 核心方法:尝试获取shared_ptr
MySharedPtr<T> lock() const {
if (expired()) {
std::cout << "MyWeakPtr::lock() - 对象已销毁" << std::endl;
return MySharedPtr<T>(); // 返回空的shared_ptr
}
// 对象还存在,创建一个新的shared_ptr
MySharedPtr<T> result;
result.m_ptr = m_ptr;
result.m_control = m_control;
if (result.m_control) {
++result.m_control->strong_count;
std::cout << "MyWeakPtr::lock() 成功,强引用: "
<< result.m_control->strong_count << std::endl;
}
return result;
}
// 9. 检查对象是否已被销毁
bool expired() const {
return !m_control || m_control->strong_count == 0;
}
// 10. 获取引用计数信息
int use_count() const {
return m_control ? m_control->strong_count : 0;
}
// 11. 获取弱引用计数
int weak_count() const {
return m_control ? m_control->weak_count : 0;
}
private:
void cleanup() {
if (m_control) {
--m_control->weak_count;
std::cout << "MyWeakPtr 清理,弱引用: "
<< m_control->weak_count << std::endl;
// 如果没有强引用也没有弱引用了,删除控制块
if (m_control->strong_count == 0 && m_control->weak_count == 0) {
delete m_control;
}
m_control = nullptr;
}
m_ptr = nullptr;
}
};
🔍 核心原理详解
1. 控制块设计
struct ControlBlock {
T* object; // 实际对象
int strong_count; // 强引用计数
int weak_count; // 弱引用计数
};
2. 弱引用不增加强计数
MyWeakPtr(const MySharedPtr<T>& shared)
: m_ptr(shared.m_ptr), m_control(shared.m_control) {
if (m_control) {
++m_control->weak_count; // 只增加弱引用计数!
}
}
3. lock() 方法的实现
MySharedPtr<T> lock() const {
if (expired()) {
return MySharedPtr<T>(); // 返回空的shared_ptr
}
// 对象还存在,创建新的shared_ptr(会增加强引用计数)
MySharedPtr<T> result;
result.m_ptr = m_ptr;
result.m_control = m_control;
if (result.m_control) {
++result.m_control->strong_count;
}
return result;
}
🧪 测试代码
class TestObject {
public:
TestObject(int id) : m_id(id) {
std::cout << "TestObject 构造 [" << m_id << "]" << std::endl;
}
~TestObject() {
std::cout << "TestObject 析构 [" << m_id << "]" << std::endl;
}
void speak() const {
std::cout << "TestObject [" << m_id << "] 还在!" << std::endl;
}
int getId() const { return m_id; }
private:
int m_id;
};
void test_basic_usage() {
std::cout << "=== 测试1: 基本用法 ===" << std::endl;
{
MySharedPtr<TestObject> shared(new TestObject(1));
MyWeakPtr<TestObject> weak = shared;
std::cout << "强引用: " << shared.use_count()
<< ", 弱引用: " << weak.weak_count() << std::endl;
if (auto locked = weak.lock()) {
locked->speak();
std::cout << "lock后强引用: " << shared.use_count() << std::endl;
}
} // shared析构,对象被销毁
}
void test_expired() {
std::cout << "\n=== 测试2: 过期检测 ===" << std::endl;
MyWeakPtr<TestObject> weak;
{
MySharedPtr<TestObject> shared(new TestObject(2));
weak = shared;
std::cout << "对象存在时 - expired: " << weak.expired()
<< ", use_count: " << weak.use_count() << std::endl;
if (auto locked = weak.lock()) {
locked->speak();
}
} // shared析构
std::cout << "对象销毁后 - expired: " << weak.expired()
<< ", use_count: " << weak.use_count() << std::endl;
if (auto locked = weak.lock()) {
std::cout << "这行不会执行" << std::endl;
} else {
std::cout << "lock() 返回空指针" << std::endl;
}
}
void test_cycle_reference_solution() {
std::cout << "\n=== 测试3: 解决循环引用 ===" << std::endl;
class NodeA;
class NodeB;
class NodeA {
public:
MySharedPtr<NodeB> b_ptr;
~NodeA() { std::cout << "NodeA 析构" << std::endl; }
};
class NodeB {
public:
MyWeakPtr<NodeA> a_weak; // 使用weak_ptr打破循环
~NodeB() { std::cout << "NodeB 析构" << std::endl; }
};
{
MySharedPtr<NodeA> a(new NodeA);
MySharedPtr<NodeB> b(new NodeB);
a->b_ptr = b; // A 强引用 B
b->a_weak = a; // B 弱引用 A(不会增加引用计数!)
std::cout << "A 强引用计数: " << a.use_count() << std::endl;
std::cout << "B 强引用计数: " << b.use_count() << std::endl;
// 检查弱引用是否可用
if (auto a_locked = b->a_weak.lock()) {
std::cout << "弱引用转强引用成功!" << std::endl;
}
} // a和b都能正常析构,没有内存泄漏!
}
void test_multiple_weak_refs() {
std::cout << "\n=== 测试4: 多个弱引用 ===" << std::endl;
MyWeakPtr<TestObject> weak1, weak2, weak3;
{
MySharedPtr<TestObject> shared(new TestObject(3));
weak1 = shared;
weak2 = shared;
weak3 = shared;
std::cout << "强引用: " << shared.use_count()
<< ", 弱引用: " << weak1.weak_count() << std::endl;
} // shared析构,对象被销毁
std::cout << "所有weak_ptr都报告expired: "
<< weak1.expired() << "," << weak2.expired() << "," << weak3.expired() << std::endl;
}
int main() {
test_basic_usage();
test_expired();
test_cycle_reference_solution();
test_multiple_weak_refs();
return 0;
}
📊 预期输出示例
=== 测试1: 基本用法 ===
TestObject 构造 [1]
MySharedPtr 构造,强引用: 1
MyWeakPtr 从shared_ptr赋值,弱引用: 1
强引用: 1, 弱引用: 1
MySharedPtr 拷贝构造,强引用: 2
TestObject [1] 还在!
lock后强引用: 2
MySharedPtr 清理,强引用: 1
MySharedPtr 清理,强引用: 0
删除托管对象
TestObject 析构 [1]
MyWeakPtr 清理,弱引用: 0
控制块被销毁
=== 测试2: 过期检测 ===
TestObject 构造 [2]
MySharedPtr 构造,强引用: 1
MyWeakPtr 从shared_ptr赋值,弱引用: 1
对象存在时 - expired: 0, use_count: 1
MySharedPtr 拷贝构造,强引用: 2
TestObject [2] 还在!
MySharedPtr 清理,强引用: 1
MySharedPtr 清理,强引用: 0
删除托管对象
TestObject 析构 [2]
对象销毁后 - expired: 1, use_count: 0
MyWeakPtr::lock() - 对象已销毁
lock() 返回空指针
MyWeakPtr 清理,弱引用: 0
控制块被销毁
=== 测试3: 解决循环引用 ===
MySharedPtr 构造,强引用: 1
MySharedPtr 构造,强引用: 1
MySharedPtr 拷贝构造,强引用: 2
MyWeakPtr 从shared_ptr赋值,弱引用: 1
A 强引用计数: 1
B 强引用计数: 2
MySharedPtr 拷贝构造,强引用: 2
弱引用转强引用成功!
MySharedPtr 清理,强引用: 1
MySharedPtr 清理,强引用: 0
NodeB 析构
MySharedPtr 清理,强引用: 0
NodeA 析构
MyWeakPtr 清理,弱引用: 0
控制块被销毁
=== 测试4: 多个弱引用 ===
TestObject 构造 [3]
MySharedPtr 构造,强引用: 1
MyWeakPtr 从shared_ptr赋值,弱引用: 1
MyWeakPtr 从shared_ptr赋值,弱引用: 2
MyWeakPtr 从shared_ptr赋值,弱引用: 3
强引用: 1, 弱引用: 3
MySharedPtr 清理,强引用: 0
删除托管对象
TestObject 析构 [3]
所有weak_ptr都报告expired: 1,1,1
MyWeakPtr 清理,弱引用: 2
MyWeakPtr 清理,弱引用: 1
MyWeakPtr 清理,弱引用: 0
控制块被销毁
🔧 与标准库的差异
我们的简化版本缺少了标准库的以下特性:
-
线程安全 - 标准库使用原子操作
-
异常安全 - 更完善的异常处理
-
类型转换 -
dynamic_pointer_cast等对weak_ptr的支持 -
哈希支持 - 可用于标准库容器
-
更高效的内存布局 - 标准库有更优化的控制块设计
💡 学到的关键点
通过这个实现,你深入理解了:
1. 弱引用的本质
// weak_ptr 不拥有对象的所有权
// 它只是对象的"观察者",不会阻止对象被销毁
2. 循环引用解决方案
class A {
std::shared_ptr<B> b_ptr; // 强引用
};
class B {
std::weak_ptr<A> a_weak; // 弱引用,打破循环
};
3. 控制块的生命周期
// 控制块比对象存活更久
// 只有当强引用和弱引用都为0时,控制块才被销毁
4. lock() 的安全语义
// lock() 是原子操作:
// 1. 检查对象是否还存在
// 2. 如果存在,增加强引用计数
// 3. 返回有效的shared_ptr或空指针
设计哲学理解:
weak_ptr体现了"观察者模式"在资源管理中的应用:
-
不干预生命周期 - 只是观察,不负责管理
-
安全访问 - 通过
lock()获得临时所有权 -
打破循环 - 解决共享所有权中的循环引用问题

浙公网安备 33010602011771号