std::shared_ptr 引用计数实现原理
- 引用计数的存储方式
std::shared_ptr
通常将引用计数存储在一个控制块(control block)中。这个控制块除了包含引用计数外,还可能包含指向被管理对象的指针以及其他相关信息,比如弱引用计数(用于 std::weak_ptr
)。
- 当创建一个
std::shared_ptr
对象指向某个动态分配的对象时,会同时创建一个控制块,并将引用计数初始化为 1。例如:
int* ptr = new int(42);
std::shared_ptr<int> sp(ptr);
这里 sp
指向 ptr
,同时创建的控制块中的引用计数为 1。
- 当进行赋值(
operator=
)或拷贝构造时,引用计数会增加。例如:
std::shared_ptr<int> sp2(sp);
此时 sp2
和 sp
共享同一个控制块,控制块中的引用计数加 1 变为 2。
- 当
std::shared_ptr
对象析构时,引用计数会减 1。如果引用计数减为 0,控制块会释放被管理的对象以及控制块自身。
- 线程安全性
- 对
std::shared_ptr
本身的多线程操作是线程安全的。也就是说,多个线程可以同时对不同的 std::shared_ptr
对象进行拷贝、赋值、析构等操作,不会出现数据竞争。
- 然而,对于指向的同一个对象的
std::shared_ptr
之间的操作,比如多个线程同时通过 std::shared_ptr
访问和修改同一个对象的成员,这仍然需要额外的同步机制(如互斥锁)来保证线程安全。
- 在多线程环境下,引用计数的增减操作通常是原子操作。例如,当一个线程增加引用计数,另一个线程减少引用计数时,不会出现中间状态导致引用计数错误。C++ 标准库通过使用原子类型(如
std::atomic<int>
)来实现引用计数的原子操作,从而保证线程安全。
简易版类似 std::shared_ptr 的自定义智能指针实现
#include <iostream>
#include <atomic>
template <typename T>
class MySharedPtr {
private:
T* ptr;
std::atomic<int>* refCount;
public:
// 构造函数
MySharedPtr(T* p = nullptr) : ptr(p) {
if (ptr) {
refCount = new std::atomic<int>(1);
} else {
refCount = new std::atomic<int>(0);
}
}
// 拷贝构造函数
MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), refCount(other.refCount) {
if (ptr) {
(*refCount)++;
}
}
// 移动构造函数
MySharedPtr(MySharedPtr&& other) noexcept : ptr(other.ptr), refCount(other.refCount) {
other.ptr = nullptr;
other.refCount = nullptr;
}
// 赋值运算符重载
MySharedPtr& operator=(const MySharedPtr& other) {
if (this == &other) {
return *this;
}
if (ptr && --(*refCount) == 0) {
delete ptr;
delete refCount;
}
ptr = other.ptr;
refCount = other.refCount;
if (ptr) {
(*refCount)++;
}
return *this;
}
// 移动赋值运算符重载
MySharedPtr& operator=(MySharedPtr&& other) noexcept {
if (this == &other) {
return *this;
}
if (ptr && --(*refCount) == 0) {
delete ptr;
delete refCount;
}
ptr = other.ptr;
refCount = other.refCount;
other.ptr = nullptr;
other.refCount = nullptr;
return *this;
}
// 析构函数
~MySharedPtr() {
if (ptr && --(*refCount) == 0) {
delete ptr;
delete refCount;
}
}
// 获取指向的对象
T* get() const {
return ptr;
}
// 获取引用计数
int use_count() const {
return *refCount;
}
// 解引用操作符重载
T& operator*() const {
return *ptr;
}
// 箭头操作符重载
T* operator->() const {
return ptr;
}
};