TypechoJoeTheme

至尊技术网

统计
登录
用户名
密码

手写简化版shared_ptr:深入理解C++引用计数智能指针

2025-07-23
/
0 评论
/
4 阅读
/
正在检测是否收录...
07/23


在C++开发中,内存管理一直是开发者必须面对的挑战。传统裸指针的显式delete操作不仅容易导致内存泄漏,还可能引发悬垂指针等问题。本文将带你从零实现一个简化版的shared_ptr,通过引用计数机制实现自动化内存管理。

一、引用计数基本原理

引用计数的核心思想是通过计数器跟踪资源被引用的次数,当计数归零时自动释放资源。这种机制需要解决三个关键问题:

  1. 计数器的存储位置(必须被所有引用共享)
  2. 线程安全性(本文示例暂不考虑)
  3. 循环引用问题(可通过weak_ptr解决,本文不涉及)

二、简化版SharedPtr实现

我们首先定义核心结构体ControlBlock来保存引用计数:

cpp
template
struct ControlBlock {
T* ptr;
sizet refcount;

explicit ControlBlock(T* p) : ptr(p), ref_count(1) {}

~ControlBlock() {
    delete ptr;
}

};

接下来实现SharedPtr类模板:

cpp
template
class SharedPtr {
ControlBlock* cb_;

// 辅助私有方法
void retain_() {
    if (cb_) ++cb_->ref_count;
}

void release_() {
    if (cb_ && --cb_->ref_count == 0) {
        delete cb_;
    }
}

public:
// 构造函数
explicit SharedPtr(T* ptr = nullptr)
: cb_(ptr ? new ControlBlock(ptr) : nullptr) {}

// 拷贝构造
SharedPtr(const SharedPtr& other) : cb_(other.cb_) {
    retain_();
}

// 移动构造
SharedPtr(SharedPtr&& other) noexcept : cb_(other.cb_) {
    other.cb_ = nullptr;
}

// 析构函数
~SharedPtr() {
    release_();
}

// 拷贝赋值
SharedPtr& operator=(const SharedPtr& other) {
    if (this != &other) {
        release_();
        cb_ = other.cb_;
        retain_();
    }
    return *this;
}

// 移动赋值
SharedPtr& operator=(SharedPtr&& other) noexcept {
    if (this != &other) {
        release_();
        cb_ = other.cb_;
        other.cb_ = nullptr;
    }
    return *this;
}

// 解引用操作符
T& operator*() const { return *cb_->ptr; }
T* operator->() const { return cb_->ptr; }

// 辅助方法
size_t use_count() const { return cb_ ? cb_->ref_count : 0; }
explicit operator bool() const { return cb_ && cb_->ptr; }

};

三、关键实现细节解析

  1. 控制块分离设计



    • 将引用计数与对象存储分离,避免侵入式设计
    • 控制块的生命周期由所有SharedPtr实例共享管理
  2. 异常安全性



    • 构造函数确保要么完全成功,要么抛出异常前清理资源
    • 赋值操作采用copy-and-swap惯用法保证强异常安全
  3. 资源释放时机



    • ref_count减到0时,自动调用控制块析构
    • 析构函数中先delete ptr再释放控制块内存

四、使用示例与测试

cpp
class MyResource {
public:
MyResource() { std::cout << "Resource created\n"; }
~MyResource() { std::cout << "Resource destroyed\n"; }
void use() { std::cout << "Resource used\n"; }
};

void testfunc() { SharedPtr p1(new MyResource()); { auto p2 = p1; // 拷贝构造 p2->use(); std::cout << "Use count: " << p1.usecount() << "\n"; // 输出2
}
std::cout << "Use count: " << p1.use_count() << "\n"; // 输出1
} // p1析构时资源自动释放

五、与标准库shared_ptr的差异

  1. 缺少线程安全保证(标准库使用原子操作)
  2. 不支持自定义删除器
  3. 未实现weak_ptr相关功能
  4. 缺少类型转换接口(staticpointercast等)
  5. 简化了异常处理逻辑

六、扩展思考

  1. 循环引用问题:可通过实现weak_ptr打破循环,需要分离引用计数与对象生命周期
  2. 性能优化:考虑使用原子操作实现线程安全版本
  3. 内存分配策略:可优化控制块与对象的连续内存分配

最佳实践提示:生产环境建议直接使用std::shared_ptr,自定义实现主要用于学习原理和特殊场景定制。

资源管理内存安全RAIIC++智能指针引用计数
朗读
赞(0)
版权属于:

至尊技术网

本文链接:

https://www.zzwws.cn/archives/33661/(转载时请注明本文出处及文章链接)

评论 (0)