C++ shared_ptr 底层实现分析
智能指针引入的原因:指针管理的困境:内存泄漏, 悬垂指针(指针指向的内存释放或重新分配内存导致),重复释放,野指针(指针未被正确初始化)
shared_ptr
是 C++ 智能指针中最重要的一个,它实现了共享所有权的概念,通过引用计数机制来管理对象的生命周期。下面我将详细分析其底层实现原理。
核心实现机制
shared_ptr
的核心实现通常包含以下几个关键部分:
控制块(Control Block):
存储引用计数(shared count)
存储弱引用计数(weak count)
存储删除器(deleter)
存储分配器(allocator)
指向被管理对象的指针
引用计数:
每当一个新的
shared_ptr
指向同一对象时,引用计数增加当
shared_ptr
被销毁或重置时,引用计数减少当引用计数降为 0 时,删除被管理对象
典型实现结构
template<typename T>
class shared_ptr {
private:T* ptr; // 指向被管理对象的指针ControlBlock* control; // 指向控制块的指针struct ControlBlock {long shared_count; // 共享引用计数long weak_count; // 弱引用计数Deleter deleter; // 删除器// 可能还有其他成员...};public:// 构造函数、析构函数、拷贝控制成员等...
};
关键操作实现
构造函数
template<typename T>
shared_ptr<T>::shared_ptr(T* p) : ptr(p), control(new ControlBlock) {if (control) {control->shared_count = 1;control->weak_count = 0;}
}
拷贝构造函数
template<typename T>
shared_ptr<T>::shared_ptr(const shared_ptr<T>& other) : ptr(other.ptr), control(other.control) {if (control) {++control->shared_count;}
}
析构函数
template<typename T>
shared_ptr<T>::~shared_ptr() {if (control) {--control->shared_count;if (control->shared_count == 0) {// 删除被管理对象control->deleter(ptr);// 如果没有弱引用,删除控制块if (control->weak_count == 0) {delete control;}}}
}
赋值操作符
template<typename T>
shared_ptr<T>& shared_ptr<T>::operator=(const shared_ptr<T>& other) {if (this != &other) {// 减少当前引用计数this->~shared_ptr();// 复制新指针和控制块ptr = other.ptr;control = other.control;if (control) {++control->shared_count;}}return *this;
}
线程安全性
现代 shared_ptr
实现通常保证:
不同
shared_ptr
实例可以被多线程同时访问(即使它们管理同一对象)同一
shared_ptr
实例的多个成员函数调用需要外部同步引用计数的增减是原子操作(通常使用原子操作或互斥锁实现)
性能考虑
内存开销:
每个
shared_ptr
对象通常需要存储两个指针(对象指针和控制块指针)控制块本身也有额外内存开销
时间开销:
引用计数的增减操作需要原子操作,比普通指针操作慢
控制块的动态分配也需要时间
自定义删除器
shared_ptr
支持自定义删除器,这在管理特殊资源时非常有用:
struct FileDeleter {void operator()(FILE* fp) const {if (fp) fclose(fp);}
};shared_ptr<FILE> filePtr(fopen("test.txt", "r"), FileDeleter());
与 weak_ptr 的交互
weak_ptr
也使用相同的控制块,但只增加弱引用计数,不影响共享引用计数。当最后一个 shared_ptr
被销毁时,对象会被删除,但控制块会保留直到所有 weak_ptr
也被销毁。
解决循环引用
class Node {
public:// shared_ptr<Node> next; // 这样会导致循环引用weak_ptr<Node> next; // 使用 weak_ptr 避免循环~Node() { cout << "Node destroyed\n"; }
};void no_circular_reference() {shared_ptr n1 = make_shared<Node>();shared_ptr n2 = make_shared<Node>();n1->next = n2;n2->next = n1; // 不会造成循环引用
}