在现代 C++ 开发中,智能指针(如 std::unique_ptr 和 std::shared_ptr)已经成为管理动态资源的重要工具。然而,在某些特殊场景下,标准库的智能指针可能无法满足所有需求,例如需要自定义引用计数策略、支持多继承或在多线程环境下实现细粒度的锁机制。下面我们将演示如何从头开始实现一个简易的自定义智能指针 MySmartPtr,并讨论其扩展点与使用场景。
1. 目标与设计原则
- 自动资源管理:当指针生命周期结束时自动销毁底层资源。
- 引用计数:支持多份指针共享同一资源,并在最后一份销毁时释放资源。
- 线程安全:采用原子操作确保多线程环境下计数器的正确性。
- 可定制销毁器:允许用户指定自定义销毁函数,以满足非
delete的资源释放方式。
2. 基本实现
#include <atomic>
#include <utility>
#include <iostream>
template <typename T, typename Deleter = std::default_delete<T>>
class MySmartPtr {
public:
// 构造器
explicit MySmartPtr(T* ptr = nullptr, Deleter del = Deleter())
: ptr_(ptr), refcnt_(new std::atomic <size_t>(1)), deleter_(del) {}
// 拷贝构造
MySmartPtr(const MySmartPtr& other)
: ptr_(other.ptr_), refcnt_(other.refcnt_), deleter_(other.deleter_) {
if (ptr_) (*refcnt_)++;
}
// 移动构造
MySmartPtr(MySmartPtr&& other) noexcept
: ptr_(other.ptr_), refcnt_(other.refcnt_), deleter_(std::move(other.deleter_)) {
other.ptr_ = nullptr;
other.refcnt_ = nullptr;
}
// 拷贝赋值
MySmartPtr& operator=(const MySmartPtr& other) {
if (this != &other) {
release(); // 先释放当前资源
ptr_ = other.ptr_;
refcnt_ = other.refcnt_;
deleter_ = other.deleter_;
if (ptr_) (*refcnt_)++; // 增加引用计数
}
return *this;
}
// 移动赋值
MySmartPtr& operator=(MySmartPtr&& other) noexcept {
if (this != &other) {
release();
ptr_ = other.ptr_;
refcnt_ = other.refcnt_;
deleter_ = std::move(other.deleter_);
other.ptr_ = nullptr;
other.refcnt_ = nullptr;
}
return *this;
}
// 析构
~MySmartPtr() { release(); }
// 解引用操作符
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
// 访问引用计数
size_t use_count() const { return ptr_ ? (*refcnt_) : 0; }
// 互斥释放
private:
void release() {
if (ptr_ && refcnt_) {
if (--(*refcnt_) == 0) {
deleter_(ptr_);
delete refcnt_;
}
ptr_ = nullptr;
refcnt_ = nullptr;
}
}
private:
T* ptr_;
std::atomic <size_t>* refcnt_;
Deleter deleter_;
};
关键点说明
- 引用计数:使用 `std::atomic ` 保证多线程环境下计数的原子性。
- 自定义销毁器:
Deleter模板参数允许用户传入任何可调用对象,例如std::function<void(T*)>或自定义结构。 - 移动语义:移动构造和赋值时将指针所有权转移,避免不必要的引用计数递增递减。
- 资源释放:
release()方法在引用计数降为零时调用销毁器并删除计数器。
3. 使用示例
int main() {
MySmartPtr <int> p1(new int(42));
std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 1
MySmartPtr <int> p2 = p1; // 拷贝
std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 2
{
MySmartPtr <int> p3 = std::move(p2); // 移动
std::cout << "p3 value: " << *p3 << std::endl;
} // p3 离开作用域,计数减一
std::cout << "p1 use_count after p3 destruct: " << p1.use_count() << std::endl; // 1
return 0;
}
4. 可扩展方向
- 弱引用:实现类似
std::weak_ptr的弱引用,避免循环引用导致的内存泄漏。 - 自定义分配器:允许用户在构造时传入自定义分配器,以支持内存池或共享内存。
- 多继承支持:在存在虚继承的类层次结构中使用
static_cast或dynamic_cast进行安全转换。 - 性能优化:采用分段计数或
std::shared_ptr的“自定义计数器”机制减少锁竞争。
5. 与标准库的比较
| 特性 | std::shared_ptr |
MySmartPtr |
|---|---|---|
| 线程安全 | 是 | 是 |
| 自定义销毁器 | 支持 | 支持 |
| 弱引用 | 支持 | 暂无 |
| 内存占用 | 3 sizeof(void) | 2 sizeof(void)(更小) |
| 对齐 & 对象池 | 不可定制 | 可定制 |
| 典型使用 | 一般共享 | 特殊需求(如自定义分配器、低内存占用) |
6. 小结
通过上面的实现,我们掌握了从零开始创建智能指针的基本思路。虽然标准库已经提供了成熟的 std::shared_ptr 与 std::unique_ptr,但在需要特殊行为或更细粒度控制时,自己实现一个自定义智能指针仍是非常有价值的练习。希望这篇文章能帮助你在 C++ 编程中更好地理解资源管理与多线程安全的底层实现细节。