在 C++ 标准库中,std::shared_ptr 和 std::unique_ptr 已经满足大多数资源管理需求,但有时我们需要一种更符合业务需求的自定义智能指针。下面将演示一个简易但功能完整的 MySharedPtr 实现,包括引用计数、线程安全、以及自定义删除器。代码仅供学习参考,实际生产环境请使用标准库或成熟的第三方实现。
#pragma once
#include <atomic>
#include <cstddef>
#include <memory>
#include <iostream>
#include <utility>
template<typename T, typename Deleter = std::default_delete<T>>
class MySharedPtr {
public:
// 构造函数:直接接受裸指针
explicit MySharedPtr(T* ptr = nullptr, Deleter del = Deleter())
: ptr_(ptr), deleter_(del) {
if (ptr_) {
ref_count_ = new std::atomic <size_t>(1);
} else {
ref_count_ = nullptr;
}
}
// 构造函数:接受已有的 MySharedPtr(复制构造)
MySharedPtr(const MySharedPtr& other) noexcept
: ptr_(other.ptr_), deleter_(other.deleter_), ref_count_(other.ref_count_) {
increment();
}
// 移动构造
MySharedPtr(MySharedPtr&& other) noexcept
: ptr_(other.ptr_), deleter_(std::move(other.deleter_)), ref_count_(other.ref_count_) {
other.ptr_ = nullptr;
other.ref_count_ = nullptr;
}
// 赋值运算符(拷贝)
MySharedPtr& operator=(const MySharedPtr& other) noexcept {
if (this != &other) {
release();
ptr_ = other.ptr_;
deleter_ = other.deleter_;
ref_count_ = other.ref_count_;
increment();
}
return *this;
}
// 赋值运算符(移动)
MySharedPtr& operator=(MySharedPtr&& other) noexcept {
if (this != &other) {
release();
ptr_ = other.ptr_;
deleter_ = std::move(other.deleter_);
ref_count_ = other.ref_count_;
other.ptr_ = nullptr;
other.ref_count_ = nullptr;
}
return *this;
}
~MySharedPtr() { release(); }
// 解引用操作
T& operator*() const noexcept { return *ptr_; }
T* operator->() const noexcept { return ptr_; }
T* get() const noexcept { return ptr_; }
// 引用计数
size_t use_count() const noexcept { return ref_count_ ? ref_count_->load() : 0; }
// 判断是否唯一所有者
bool unique() const noexcept { return use_count() == 1; }
private:
void increment() noexcept {
if (ref_count_) ref_count_->fetch_add(1, std::memory_order_relaxed);
}
void release() noexcept {
if (ref_count_ && ref_count_->fetch_sub(1, std::memory_order_acq_rel) == 1) {
deleter_(ptr_);
delete ref_count_;
}
}
T* ptr_;
Deleter deleter_;
std::atomic <size_t>* ref_count_;
};
关键点说明
-
引用计数
- 使用 `std::atomic ` 以保证多线程环境下计数安全。
fetch_add/fetch_sub与memory_order_relaxed或acq_rel保证正确的同步。
-
自定义删除器
- 模板参数
Deleter默认使用 `std::default_delete `。 - 允许用户传入 lambda 或自定义 functor 来控制资源释放逻辑,例如:
MySharedPtr<MyFile, FileCloser>(filePtr, FileCloser{})。
- 模板参数
-
构造/析构/赋值
- 复制构造时计数加 1;移动构造时转移所有权。
- 赋值运算符先释放自身资源,再做复制或移动。
-
使用方式
struct MyStruct { int x; }; MySharedPtr <MyStruct> p1(new MyStruct{10}); MySharedPtr <MyStruct> p2 = p1; // 共享所有权 std::cout << p1.use_count() << std::endl; // 输出 2
与标准库的区别
- 性能:标准库实现使用控制块来分离数据和引用计数,进一步优化。
- 异常安全:标准库保证异常安全,本文示例简化了错误处理。
- 特性:标准库提供
weak_ptr、自定义控制块、make_shared等高级功能。
适用场景
- 学习指针管理和 RAII 的实现细节。
- 在不允许引入 ` `(如古老编译器或特殊项目)时使用。
- 需要特定的删除逻辑但不想使用标准库的完整实现。
小结
自定义智能指针可以帮助我们更深入理解 C++ 内存管理机制。通过模板化的删除器、线程安全的引用计数以及完整的生命周期管理,MySharedPtr 能够在多种环境下安全地共享资源。若项目规模较大或需要更完善的功能,建议直接使用 std::shared_ptr,以减少维护成本和潜在 bug。