原理
shared_ptr
在底层实现上,维护一个引用计数,来管理内存对象的生命周期:当新构造一个对象时,引用计数初始化为1,拷贝对象时,引用计数加1,对象作用域结束析构时,引用计数减1,当最后一个对象被销毁时,引用计数会减为0,所持有的资源被释放!
线程安全性
shared_ptr
保证多个线程能够安全地增加或减少其引用计数。但如果多个线程同时读写同一个shared_ptr
或操作其管理的对象,那么需要额外进行同步机制,比如使用互斥锁mutex
来保护这些操作。
手写一个shared_ptr
shared_ptr
内部包含两部分指针,一个指向实际管理的资源,另一个指向控制块。控制块中包含引用计数,当shared_ptr
被拷贝的时候,引用计数增加,销毁的时候引用计数减少,当引用计数变为0的时候,资源被释放!
cpp
// 管理引用计数的类
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_;
// 引用计数减1,若最终引用计数减至0,则释放指针
void clean()
{
if (ref_count_)
{
ref_count_->minus();
if (ref_count_->get() == 0)
{
if (ptr_)
delete ptr_;
delete ref_count_;
}
}
}
};
shared_ptr
的交叉引用问题
cpp
#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
解决交叉引用问题!
cpp
#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
只能观察资源,而无法使用资源。
cpp
#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()
{
// _ptra->testA(); // 弱智能指针无法直接使用资源
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;
}