C++ - 简单实现std::weak_ptr

我们无法直接模拟std::weak_ptr,因为它的功能依赖于std::shared_ptr的控制块。但是,我们可以通过扩展之前实现的MySharedPtr来模拟weak_ptr的基本行为。

思路:

  1. 在MySharedPtr的控制块中增加一个弱引用计数。

  2. MyWeakPtr将包含与控制块相同的指针,但不会增加强引用计数,只会增加弱引用计数。

  3. 当强引用计数为0时,删除托管对象,但控制块要等到弱引用计数也为0时才删除。

  4. MyWeakPtr可以通过lock()方法获得一个MySharedPtr,如果对象还存在则增加强引用计数,否则返回一个空的MySharedPtr。

我们先修改MySharedPtr,增加对弱引用的支持,然后实现MyWeakPtr。

步骤:

  1. 修改控制块,包含强引用计数和弱引用计数。

  2. 修改MySharedPtr,使其支持弱引用。

  3. 实现MyWeakPtr。

注意:由于我们之前实现的MySharedPtr是使用单独的int*作为引用计数,现在我们需要一个结构体来同时保存强引用和弱引用计数。

控制块结构体:

cpp
 
 
 
 
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来支持控制块:

cpp
 
 
 
 
#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

cpp
 
 
 
 
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. 控制块设计

cpp
 
 
 
 
struct ControlBlock {
    T* object;        // 实际对象
    int strong_count; // 强引用计数
    int weak_count;   // 弱引用计数
};

2. 弱引用不增加强计数

cpp
 
 
 
 
MyWeakPtr(const MySharedPtr<T>& shared) 
    : m_ptr(shared.m_ptr), m_control(shared.m_control) {
    if (m_control) {
        ++m_control->weak_count;  // 只增加弱引用计数!
    }
}

3. lock() 方法的实现

cpp
 
 
 
 
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;
}

🧪 测试代码

cpp
 
 
 
 
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;
}

📊 预期输出示例

text
 
 
 
 
=== 测试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. 弱引用的本质

cpp
 
 
 
 
// weak_ptr 不拥有对象的所有权
// 它只是对象的"观察者",不会阻止对象被销毁

2. 循环引用解决方案

cpp
 
 
 
 
class A {
    std::shared_ptr<B> b_ptr;  // 强引用
};

class B {
    std::weak_ptr<A> a_weak;   // 弱引用,打破循环
};

3. 控制块的生命周期

cpp
 
 
 
 
// 控制块比对象存活更久
// 只有当强引用和弱引用都为0时,控制块才被销毁

4. lock() 的安全语义

cpp
 
 
 
 
// lock() 是原子操作:
// 1. 检查对象是否还存在
// 2. 如果存在,增加强引用计数
// 3. 返回有效的shared_ptr或空指针

设计哲学理解

weak_ptr体现了"观察者模式"在资源管理中的应用:

  • 不干预生命周期 - 只是观察,不负责管理

  • 安全访问 - 通过lock()获得临时所有权

  • 打破循环 - 解决共享所有权中的循环引用问题

posted @ 2025-11-25 15:53  [BORUTO]  阅读(12)  评论(0)    收藏  举报