原理
shared_ptr
在底层实现上,维护一个引用计数,来管理内存对象的生命周期:当新构造一个对象时,引用计数初始化为1,拷贝对象时,引用计数加1,对象作用域结束析构时,引用计数减1,当最后一个对象被销毁时,引用计数会减为0,所持有的资源被释放!
线程安全性
shared_ptr
保证多个线程能够安全地增加或减少其引用计数。但如果多个线程同时读写同一个shared_ptr
或操作其管理的对象,那么需要额外进行同步机制,比如使用互斥锁mutex
来保护这些操作。
手写一个shared_ptr
shared_ptr
内部包含两部分指针,一个指向实际管理的资源,另一个指向控制块。控制块中包含引用计数,当shared_ptr
被拷贝的时候,引用计数增加,销毁的时候引用计数减少,当引用计数变为0的时候,资源被释放!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| class SharedCount { public: SharedCount() : count_{1} {};
void add() { ++count_; }
void minus() { --count_; }
int get() const { return count_; } private: std::atomic<int> count_; };
template<typename T> class SharedPtr { public: SharedPtr(T* ptr) : ptr_{ptr}, ref_count_{new SharedCount} {}
SharedPtr() : ptr_{nullptr}, ref_count_{new SharedCount} {}
SharedPtr(const SharedPtr& p) { this->ptr_ = p.ptr_; this->ref_count_ = p.ref_count_; ref_count_->add(); }
SharedPtr& operator=(const SharedPtr& p) { if (&p == this) return *this; clean(); this->ptr_ = p.ptr_; this->ref_count_ = p.ref_count_; ref_count_->add(); return *this; }
SharedPtr(SharedPtr&& p) { this->ptr_ = p.ptr_; this->ref_count_ = p.ref_count_; p.ptr_ = nullptr; p.ref_count_ = nullptr; }
SharedPtr& operator=(SharedPtr && p) { if (&p == this) return *this; clean(); this->ptr_ = p.ptr_; this->ref_count_ = p.ref_count_; p.ptr_ = nullptr; p.ref_count_ = nullptr; return *this; }
~SharedPtr { clean(); } private: T* ptr_; SharedCount* ref_count_;
void clean() { if (ref_count_) { ref_count_->minus(); if (ref_count_->get() == 0) { if (ptr_) delete ptr_; delete ref_count_; } } } };
|
shared_ptr
的交叉引用问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <iostream> #include <memory>
using namespace std;
class B; class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } shared_ptr<B> _ptrb; };
class B { public: B() { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; } shared_ptr<A> _ptra; };
int main() { shared_ptr<A> pa(new A()); shared_ptr<B> pb(new B());
pa->_ptrb = pb; pb->_ptra = pa;
cout << pa.use_count() << endl; cout << pb.use_count() << endl;
return 0; }
|
- 以上代码输出
A() B() 2 2
,显然强智能指针管理的资源未能释放,这是因为程序结束时,强智能指针管理的资源计数-1不为0,因此无法释放。
weak_ptr
解决交叉引用问题
shared_ptr
交叉引用会导致new
的资源无法释放,从而造成资源泄露问题!→ 注意到weak_ptr
并不改变资源的引用计数,相当于”观察者“,故可以用weak_ptr
解决交叉引用问题!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <iostream> #include <memory>
using namespace std;
class B; class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } weak_ptr<B> _ptrb; };
class B { public: B() { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; } weak_ptr<A> _ptra; };
int main() { shared_ptr<A> pa(new A()); shared_ptr<B> pb(new B()); pa->_ptrb = pb; pb->_ptra = pa;
cout << pa.use_count() << endl; cout << pb.use_count() << endl;
return 0; }
|
weak_ptr
调用其它类中的方法
实际场景中,利用弱智能指针的特性解决了资源泄露问题,但是类B想通过智能指针调用类A中的一个方法,并不能直接调用,因为弱智能指针weak_ptr
只能观察资源,而无法使用资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <iostream> #include <memory>
using namespace std;
class B; class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } void testA() { cout << "testA()" << endl; } weak_ptr<B> _ptrb; };
class B { public: B() { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; } void func() { shared_ptr<A> ps = _ptra.lock(); if (ps != nullptr) { ps->testA(); } } weak_ptr<A> _ptra; };
int main() { shared_ptr<A> pa(new A()); shared_ptr<B> pb(new B());
pa->_ptrb = pb; pb->_ptra = pa;
pb->func();
cout << pa.use_count() << endl; cout << pb.use_count() << endl;
return 0; }
|