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

我们手动实现一个简化版的shared_ptr,主要实现引用计数功能。注意,这只是一个教学示例,实际标准库的实现要复杂得多,考虑了线程安全、自定义删除器、类型转换等很多特性。

我们要实现的核心功能:

  1. 构造函数:从原始指针构造

  2. 拷贝构造函数:增加引用计数

  3. 赋值操作符:减少左值的引用计数,并增加右值的引用计数

  4. 析构函数:减少引用计数,当计数为0时销毁对象

  5. 解引用操作:operator* 和 operator->

  6. 获取引用计数:use_count()

我们要实现以下核心功能:

  • 引用计数管理

  • 拷贝构造和赋值

  • 解引用操作

  • 资源自动释放

MySharedPtr.h头文件:

#include <iostream>
#include <utility> // for std::swap


template<typename T>
class MySharedPtr 
{
private:
    T* m_ptr;           // 托管对象的原始指针
    int* m_refCount;    // 引用计数指针

public:
    // 1. 默认构造函数
    MySharedPtr() : m_ptr(nullptr), m_refCount(nullptr) {}

    // 2. 从原始指针构造
    explicit MySharedPtr(T* ptr) : m_ptr(ptr), m_refCount(new int(1)) {
        std::cout << "构造函数: 引用计数 = 1" << std::endl;
    }

    // 3. 拷贝构造函数 - 核心!
    MySharedPtr(const MySharedPtr& other)
        : m_ptr(other.m_ptr), m_refCount(other.m_refCount) {
        if (m_refCount) {
            ++(*m_refCount);
            std::cout << "拷贝构造: 引用计数 = " << *m_refCount << std::endl;
        }
    }

    // 4. 拷贝赋值运算符 - 最复杂!
    MySharedPtr& operator=(const MySharedPtr& other) {
        // 处理自赋值情况:ptr1 = ptr1;
        if (this == &other) {
            return *this;
        }

        // 先减少当前对象的引用计数
        cleanup();

        // 复制数据和引用计数
        m_ptr = other.m_ptr;
        m_refCount = other.m_refCount;

        // 增加引用计数
        if (m_refCount) {
            ++(*m_refCount);
            std::cout << "拷贝赋值: 引用计数 = " << *m_refCount << std::endl;
        }

        return *this;
    }

    // 5. 移动构造函数 (C++11)
    MySharedPtr(MySharedPtr&& other) noexcept
        : m_ptr(other.m_ptr), m_refCount(other.m_refCount) {
        // 转移所有权后,将原对象置为空
        other.m_ptr = nullptr;
        other.m_refCount = nullptr;
        std::cout << "移动构造: 转移所有权" << std::endl;
    }

    // 6. 移动赋值运算符 (C++11)
    MySharedPtr& operator=(MySharedPtr&& other) noexcept {
        if (this == &other) {
            return *this;
        }

        cleanup();

        m_ptr = other.m_ptr;
        m_refCount = other.m_refCount;

        other.m_ptr = nullptr;
        other.m_refCount = nullptr;

        std::cout << "移动赋值: 转移所有权" << std::endl;
        return *this;
    }

    // 7. 析构函数
    ~MySharedPtr() {
        cleanup();
    }

    // 8. 解引用操作符
    T& operator*() const {
        return *m_ptr;
    }

    // 9. 箭头操作符
    T* operator->() const {
        return m_ptr;
    }

    // 10. 获取原始指针
    T* get() const {
        return m_ptr;
    }

    // 11. 获取引用计数
    int use_count() const {
        return m_refCount ? *m_refCount : 0;
    }

    // 12. 判断是否唯一所有者
    bool unique() const {
        return use_count() == 1;
    }

    // 13. 重置功能
    void reset(T* ptr = nullptr) {
        cleanup();
        if (ptr) {
            m_ptr = ptr;
            m_refCount = new int(1);
        }
    }

private:
    // 清理资源的核心函数
    void cleanup() {
        if (m_refCount) {
            --(*m_refCount);
            std::cout << "清理资源: 引用计数 = " << *m_refCount << std::endl;

            if (*m_refCount == 0) {
                std::cout << "删除托管对象" << std::endl;
                delete m_ptr;
                delete m_refCount;
            }
        }
        m_ptr = nullptr;
        m_refCount = nullptr;
    }
};

 

核心原理详解

1. 引用计数机制

// 关键数据结构
T* m_ptr;           // 指向托管对象
int* m_refCount;    // 指向引用计数器

// 所有拷贝都共享这两个指针
MySharedPtr(const MySharedPtr& other) 
    : m_ptr(other.m_ptr), m_refCount(other.m_refCount) {
    ++(*m_refCount);  // 引用计数+1
}

2. 资源清理逻辑

void cleanup() {
    if (m_refCount) {
        --(*m_refCount);           // 计数-1
        if (*m_refCount == 0) {    // 如果没人使用了
            delete m_ptr;          // 删除实际对象
            delete m_refCount;     // 删除计数器
        }
    }
}

3. 拷贝赋值的精妙处理

MySharedPtr& operator=(const MySharedPtr& other) {
    if (this == &other) return *this;  // 自赋值检查
    
    cleanup();        // 先清理当前资源
    // ... 然后复制新资源
}

 

测试代码

main.cpp

#include <iostream>

#include "MySharedPtr.h"

// 测试类
class TestObject 
{
public:
    TestObject(std::string name,int val = 0) : m_name(name),m_value(val)
    {
        std::cout << "TestObject 构造: " <<"m_name = " << m_name << " m_value = " << m_value << std::endl;
    }

    ~TestObject() 
    {
        std::cout << "TestObject 析构: " << "m_name = " << m_name << " m_value = " << m_value << std::endl;
    }

    void print() const 
    {
        std::cout << "TestObject print函数: " << "m_name = " << m_name << " m_value = " << m_value << std::endl;
    }

private:
    std::string m_name;
    int m_value;
};



int main()
{

    std::cout << "=== 测试开始 ===" << std::endl;

    // 测试1: 基本构造
    {
        TestObject* obj_1 = new TestObject("测试类", 100);
        MySharedPtr<TestObject> ptr0{ obj_1 };
        ptr0->print();
        std::cout << "ptr0 计数: " << ptr0.use_count() << std::endl;
        MySharedPtr<TestObject> ptr1{ ptr0 };
        std::cout << "ptr0 计数: " << ptr0.use_count() << std::endl;
        std::cout << "ptr1 计数: " << ptr1.use_count() << std::endl;
    }

    // 测试1: 基本构造和析构
    {
        std::cout << "\n--- 测试1: 基本生命周期 ---" << std::endl;
        MySharedPtr<TestObject> ptr1(new TestObject("张三",100));
        ptr1->print();
    }

     // 测试2: 拷贝构造
    {
        std::cout << "\n--- 测试2: 拷贝构造 ---" << std::endl;
        MySharedPtr<TestObject> ptr1(new TestObject("李四", 200));
        MySharedPtr<TestObject> ptr2 = ptr1;  // 拷贝构造
        std::cout << "ptr1 计数: " << ptr1.use_count() << std::endl;
        std::cout << "ptr2 计数: " << ptr2.use_count() << std::endl;
    }

    // 测试3: 拷贝赋值
    {
        std::cout << "\n--- 测试3: 拷贝赋值 ---" << std::endl;
        MySharedPtr<TestObject> ptr1(new TestObject("王五",300));
        MySharedPtr<TestObject> ptr2(new TestObject("赵六",400));

        ptr2 = ptr1;  // 拷贝赋值

        std::cout << "赋值后计数: " << ptr1.use_count() << std::endl;
    }

    // 测试4: 移动语义
    {
        std::cout << "\n--- 测试4: 移动语义 ---" << std::endl;
        MySharedPtr<TestObject> ptr1(new TestObject("秦始皇",500));
        MySharedPtr<TestObject> ptr2 = std::move(ptr1);  // 移动构造

        std::cout << "移动后ptr1计数: " << ptr1.use_count() << std::endl;
        std::cout << "移动后ptr2计数: " << ptr2.use_count() << std::endl;
    }

    std::cout << "\n=== 测试结束 ===" << std::endl;



	return 0;
}

输出结果:

image

 

与标准库的差异

我们的简化版本缺少了标准库的以下特性:

  • 线程安全 - 标准库使用原子操作保证线程安全

  • 自定义删除器 - 支持不同的资源释放策略

  • 弱指针支持 - 与weak_ptr的交互

  • 类型转换 - dynamic_pointer_cast

  • 异常安全 - 更完善的异常处理

  • 数组特化 - shared_ptr<T[]>

 

通过这个实现,你深入理解了:

  1. 引用计数原理 - 多个智能指针如何共享所有权

  2. RAII思想 - 资源获取即初始化

  3. 拷贝控制 - 拷贝构造/赋值的正确处理

  4. 自赋值安全 - ptr = ptr 情况的处理

  5. 移动语义 - C++11的现代特性应用

 

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