自定义智能指针是学习 C++ 内存管理机制的关键一步。它不仅能让你对引用计数、资源归还有更深的理解,还能帮助你在需要特定行为时,创建满足业务需求的指针类型。下面将从设计思路、核心功能、实现细节以及使用示例四个方面,完整阐述如何编写一个简洁且安全的自定义智能指针。
1. 设计思路
- 目标:实现一个支持共享所有权的智能指针,类似
std::shared_ptr,但不依赖 STL。 - 核心特性
- 共享计数:多份指针共享同一资源,计数递增/递减。
- 自动销毁:计数归零时自动释放资源。
- 线程安全:计数操作需原子化。
- 简易接口:构造、析构、拷贝、移动、解引用等。
- 资源管理:为简化演示,使用
int或自定义结构体作为托管对象。
2. 核心类结构
class MySharedPtr {
public:
// 构造函数
explicit MySharedPtr(T* ptr = nullptr);
// 拷贝构造
MySharedPtr(const MySharedPtr& other);
// 移动构造
MySharedPtr(MySharedPtr&& other) noexcept;
// 析构函数
~MySharedPtr();
// 拷贝赋值
MySharedPtr& operator=(const MySharedPtr& other);
// 移动赋值
MySharedPtr& operator=(MySharedPtr&& other) noexcept;
// 访问操作符
T& operator*() const;
T* operator->() const;
// 计数查询
long use_count() const noexcept;
bool unique() const noexcept;
bool operator==(const MySharedPtr& rhs) const noexcept;
bool operator!=(const MySharedPtr& rhs) const noexcept;
private:
void release(); // 计数递减,可能销毁资源
void add_ref(); // 计数递增
T* ptr_; // 实际托管对象
std::atomic <long>* ref_; // 原子计数指针
};
ptr_保存实际托管对象。ref_指向原子计数,保证多线程下计数安全。
3. 关键实现细节
#include <atomic>
#include <utility>
template <typename T>
MySharedPtr <T>::MySharedPtr(T* ptr) : ptr_(ptr), ref_(nullptr) {
if (ptr) {
ref_ = new std::atomic <long>(1);
}
}
template <typename T>
MySharedPtr <T>::MySharedPtr(const MySharedPtr& other)
: ptr_(other.ptr_), ref_(other.ref_) {
add_ref();
}
template <typename T>
MySharedPtr <T>::MySharedPtr(MySharedPtr&& other) noexcept
: ptr_(other.ptr_), ref_(other.ref_) {
other.ptr_ = nullptr;
other.ref_ = nullptr;
}
template <typename T>
MySharedPtr <T>::~MySharedPtr() {
release();
}
template <typename T>
void MySharedPtr <T>::add_ref() {
if (ref_) ref_->fetch_add(1, std::memory_order_relaxed);
}
template <typename T>
void MySharedPtr <T>::release() {
if (ref_ && ref_->fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete ptr_;
delete ref_;
}
}
- 构造:若
ptr非空,分配计数并初始化为 1。 - 拷贝:共享计数递增。
- 移动:转移所有权,源对象置空。
- 析构:计数递减,若为 0 则销毁托管对象和计数。
4. 使用示例
#include <iostream>
struct Data {
int value;
Data(int v) : value(v) { std::cout << "Data(" << value << ") constructed\n"; }
~Data() { std::cout << "Data(" << value << ") destroyed\n"; }
};
int main() {
MySharedPtr <Data> p1(new Data(42));
std::cout << "use_count: " << p1.use_count() << '\n';
{
MySharedPtr <Data> p2 = p1; // 拷贝
std::cout << "p2 use_count: " << p2.use_count() << '\n';
std::cout << "p1->value: " << p1->value << '\n';
} // p2 离开作用域
std::cout << "after p2 scope, use_count: " << p1.use_count() << '\n';
return 0;
}
运行结果示例:
Data(42) constructed
use_count: 1
p2 use_count: 2
p1->value: 42
after p2 scope, use_count: 1
Data(42) destroyed
5. 扩展与注意事项
- 自定义删除器:可以在构造时接受一个删除器,类似
std::shared_ptr的Deleter。 - 弱引用:实现
MyWeakPtr与MySharedPtr配合,避免循环引用。 - 性能优化:使用
std::shared_ptr内部的单块内存分配可减少内存碎片。 - 异常安全:构造时若分配计数失败,需抛出异常;在拷贝/移动过程中使用
try-catch保证资源不泄漏。
6. 小结
自定义智能指针是理解 C++ 内存管理与 RAII(资源获取即初始化)理念的重要实践。通过上述实现,你可以看到计数器如何确保资源在最后一个指针释放时被正确销毁,以及原子操作如何保证多线程环境下的安全。接下来可以尝试扩展功能:实现 unique_ptr 的移植、弱引用 weak_ptr,或添加线程池等高级特性,以进一步加深对 C++ 内存模型的认识。