在现代C++中,智能指针是管理动态资源的核心工具,标准库提供了std::unique_ptr、std::shared_ptr和std::weak_ptr等实现。然而,实际项目中常常需要根据业务场景自定义智能指针,例如添加额外的引用计数策略、内存池管理或线程安全控制。下面我们将从头实现一个简易的自定义智能指针,支持共享引用计数和弱引用。
1. 设计目标
- 共享引用计数:多对象共享同一资源,资源在最后一个强引用销毁时释放。
- 弱引用:不影响引用计数,只能检查资源是否仍存在,且可在资源已释放后自动置空。
- 线程安全:使用原子操作保证计数器在多线程环境下安全更新。
- 可自定义分配器:通过模板参数指定资源分配策略。
2. 基础结构
#include <atomic>
#include <memory>
#include <iostream>
#include <type_traits>
template<typename T, typename Deleter = std::default_delete<T>>
class SharedPtr;
template<typename T>
class WeakPtr;
3. 共享控制块
控制块存放引用计数、弱计数以及删除器。
template<typename T, typename Deleter>
class ControlBlock {
public:
explicit ControlBlock(T* ptr, Deleter del)
: ptr_(ptr), deleter_(std::move(del)), strong_(1), weak_(0) {}
void add_strong() noexcept { strong_.fetch_add(1, std::memory_order_relaxed); }
void release_strong() noexcept {
if (strong_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
deleter_(ptr_);
release_weak(); // weak count includes the shared pointer itself
}
}
void add_weak() noexcept { weak_.fetch_add(1, std::memory_order_relaxed); }
void release_weak() noexcept {
if (weak_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete this;
}
}
T* get() const noexcept { return ptr_; }
private:
T* ptr_;
Deleter deleter_;
std::atomic <size_t> strong_;
std::atomic <size_t> weak_;
};
4. SharedPtr 实现
template<typename T, typename Deleter>
class SharedPtr {
public:
using element_type = T;
// 默认构造为空指针
constexpr SharedPtr() noexcept : cb_(nullptr) {}
explicit SharedPtr(T* ptr, Deleter del = Deleter{}) {
cb_ = new ControlBlock<T, Deleter>(ptr, std::move(del));
}
// 拷贝构造
SharedPtr(const SharedPtr& other) noexcept : cb_(other.cb_) {
if (cb_) cb_->add_strong();
}
// 移动构造
SharedPtr(SharedPtr&& other) noexcept : cb_(other.cb_) {
other.cb_ = nullptr;
}
// 析构
~SharedPtr() { release(); }
// 拷贝赋值
SharedPtr& operator=(const SharedPtr& other) noexcept {
if (this != &other) {
release();
cb_ = other.cb_;
if (cb_) cb_->add_strong();
}
return *this;
}
// 移动赋值
SharedPtr& operator=(SharedPtr&& other) noexcept {
if (this != &other) {
release();
cb_ = other.cb_;
other.cb_ = nullptr;
}
return *this;
}
// 访问操作符
T& operator*() const noexcept { return *cb_->get(); }
T* operator->() const noexcept { return cb_->get(); }
T* get() const noexcept { return cb_ ? cb_->get() : nullptr; }
size_t use_count() const noexcept { return cb_ ? cb_->strong_.load(std::memory_order_relaxed) : 0; }
explicit operator bool() const noexcept { return get() != nullptr; }
// 生成弱指针
WeakPtr <T> weak_from_this() const noexcept {
return WeakPtr <T>(*this);
}
private:
ControlBlock<T, Deleter>* cb_;
void release() noexcept {
if (cb_) cb_->release_strong();
}
friend class WeakPtr <T>;
};
5. WeakPtr 实现
template<typename T>
class WeakPtr {
public:
constexpr WeakPtr() noexcept : cb_(nullptr) {}
// 从 SharedPtr 构造
WeakPtr(const SharedPtr<T, std::default_delete<T>>& sp) noexcept : cb_(sp.cb_) {
if (cb_) cb_->add_weak();
}
// 拷贝构造
WeakPtr(const WeakPtr& other) noexcept : cb_(other.cb_) {
if (cb_) cb_->add_weak();
}
// 移动构造
WeakPtr(WeakPtr&& other) noexcept : cb_(other.cb_) {
other.cb_ = nullptr;
}
// 析构
~WeakPtr() { release(); }
// 拷贝赋值
WeakPtr& operator=(const WeakPtr& other) noexcept {
if (this != &other) {
release();
cb_ = other.cb_;
if (cb_) cb_->add_weak();
}
return *this;
}
// 移动赋值
WeakPtr& operator=(WeakPtr&& other) noexcept {
if (this != &other) {
release();
cb_ = other.cb_;
other.cb_ = nullptr;
}
return *this;
}
// 尝试生成 SharedPtr
SharedPtr<T, std::default_delete<T>> lock() const noexcept {
if (cb_ && cb_->strong_.load(std::memory_order_acquire) > 0) {
// 先增加 strong 计数,再返回 SharedPtr
cb_->add_strong();
return SharedPtr<T, std::default_delete<T>>(cb_);
}
return SharedPtr<T, std::default_delete<T>>(); // 空指针
}
bool expired() const noexcept {
return !cb_ || cb_->strong_.load(std::memory_order_acquire) == 0;
}
size_t use_count() const noexcept {
return cb_ ? cb_->strong_.load(std::memory_order_relaxed) : 0;
}
private:
// 用于内部直接构造 SharedPtr
template<typename, typename>
friend class SharedPtr;
ControlBlock<T, std::default_delete<T>>* cb_;
void release() noexcept {
if (cb_) cb_->release_weak();
}
};
6. 简单测试
int main() {
auto sp1 = SharedPtr <int>(new int(42));
std::cout << "sp1 use_count: " << sp1.use_count() << '\n';
auto wp = sp1.weak_from_this();
std::cout << "weak expired? " << wp.expired() << '\n';
{
auto sp2 = wp.lock();
std::cout << "sp2 value: " << *sp2 << '\n';
std::cout << "sp1 use_count: " << sp1.use_count() << '\n';
} // sp2 离开作用域,计数减一
std::cout << "sp1 use_count after sp2 destroyed: " << sp1.use_count() << '\n';
sp1 = nullptr; // 资源释放
std::cout << "wp expired? " << wp.expired() << '\n';
}
7. 进一步扩展
- 自定义内存池:在
ControlBlock中引入自定义分配器,支持内存复用。 - 引用计数策略:可替换为轻量级计数(如
std::shared_ptr的use_count只保留 strong 计数)或更复杂的thread-safe方案。 - 回调机制:在资源销毁前调用用户提供的回调,适用于资源管理器模式。
通过上述实现,你可以在项目中使用自定义的智能指针,满足更细粒度的资源控制需求,同时保持与标准库接口相似的使用体验。