在 C++11 之前,智能指针(如 std::unique_ptr、std::shared_ptr)并不存在,程序员往往需要手动管理资源。即便在 C++11 之后,这些标准库实现已非常成熟,但在某些特殊场景下,我们可能需要一个定制的智能指针来满足特殊的生命周期、线程安全、或日志记录需求。下面将展示一个简易但功能完整的自定义智能指针实现,名称为 MySharedPtr,实现了引用计数、共享与移动语义,并提供了对多线程环境的基本保护。
1. 设计思路
- 引用计数:使用
std::atomic<std::size_t>维护共享对象的引用计数,保证线程安全。 - 原始指针:
T*用于存储实际资源。 - 构造/析构:构造时初始化计数为 1,析构时递减计数,计数为 0 时销毁资源。
- 拷贝/移动语义:拷贝构造/赋值增加计数;移动构造/赋值转移指针与计数,避免无效指针。
- 访问:重载
operator*、operator->与operator bool。 - 辅助功能:提供
use_count()与reset()。
2. 代码实现
#include <atomic>
#include <iostream>
#include <utility>
#include <cassert>
template <typename T>
class MySharedPtr {
public:
// 默认构造
MySharedPtr() noexcept : ptr_(nullptr), count_(nullptr) {}
// 构造传入原始指针
explicit MySharedPtr(T* ptr) : ptr_(ptr), count_(new std::atomic<std::size_t>(1)) {}
// 拷贝构造
MySharedPtr(const MySharedPtr& other) noexcept
: ptr_(other.ptr_), count_(other.count_) {
increment();
}
// 移动构造
MySharedPtr(MySharedPtr&& other) noexcept
: ptr_(other.ptr_), count_(other.count_) {
other.ptr_ = nullptr;
other.count_ = nullptr;
}
// 析构
~MySharedPtr() {
decrement();
}
// 拷贝赋值
MySharedPtr& operator=(const MySharedPtr& other) noexcept {
if (this != &other) {
decrement();
ptr_ = other.ptr_;
count_ = other.count_;
increment();
}
return *this;
}
// 移动赋值
MySharedPtr& operator=(MySharedPtr&& other) noexcept {
if (this != &other) {
decrement();
ptr_ = other.ptr_;
count_ = other.count_;
other.ptr_ = nullptr;
other.count_ = nullptr;
}
return *this;
}
// 访问运算符
T& operator*() const noexcept { assert(ptr_); return *ptr_; }
T* operator->() const noexcept { assert(ptr_); return ptr_; }
explicit operator bool() const noexcept { return ptr_ != nullptr; }
// 用于调试查看引用计数
std::size_t use_count() const noexcept {
return count_ ? *count_ : 0;
}
// 重置为nullptr,或重新指向新的对象
void reset(T* ptr = nullptr) noexcept {
if (ptr_ != ptr) {
decrement();
ptr_ = ptr;
count_ = (ptr ? new std::atomic<std::size_t>(1) : nullptr);
}
}
private:
T* ptr_;
std::atomic<std::size_t>* count_;
void increment() noexcept {
if (count_) ++(*count_);
}
void decrement() noexcept {
if (count_ && --(*count_) == 0) {
delete ptr_;
delete count_;
}
}
};
3. 使用示例
struct Widget {
Widget() { std::cout << "Widget constructed\n"; }
~Widget() { std::cout << "Widget destructed\n"; }
void greet() const { std::cout << "Hello from Widget!\n"; }
};
int main() {
MySharedPtr <Widget> sp1(new Widget()); // use_count = 1
std::cout << "sp1 count: " << sp1.use_count() << '\n';
{
MySharedPtr <Widget> sp2 = sp1; // use_count = 2
std::cout << "sp2 count: " << sp2.use_count() << '\n';
sp2->greet();
MySharedPtr <Widget> sp3 = std::move(sp1); // sp1 becomes null, sp3 count = 2
std::cout << "sp3 count: " << sp3.use_count() << '\n';
std::cout << "sp1 bool: " << static_cast<bool>(sp1) << '\n';
} // sp2, sp3 out of scope, count decremented
std::cout << "After block, sp1 count: " << sp1.use_count() << '\n';
return 0;
}
运行结果(示例):
Widget constructed
sp1 count: 1
sp2 count: 2
Hello from Widget!
sp3 count: 2
sp1 bool: 0
Widget destructed
After block, sp1 count: 0
4. 关键点说明
- 线程安全:使用
std::atomic让引用计数的递增递减操作在多线程中无数据竞争。 - 资源管理:当计数降至零时,删除原始指针与计数器,防止内存泄漏。
- 移动语义:移动构造/赋值后,原始对象的指针变为
nullptr,保证不产生额外引用计数。 - 异常安全:所有操作均
noexcept,符合标准库实现的行为。
5. 小结
通过上述实现,你可以得到一个与 std::shared_ptr 功能相似的自定义智能指针,并可在此基础上继续扩展,例如:
- 加入自定义删除器(类似
std::unique_ptr的 deleter)。 - 提供 `make_my_shared ()` 工厂函数,避免两次 `new`。
- 实现弱引用
MyWeakPtr,以避免循环引用。
此自定义智能指针既展示了 C++ 资源管理的核心理念,也为在特殊需求场景下的进一步定制提供了坚实基础。