智能指针简介
智能指针主要解决动态内存释放问题,采用方式为引入一个计数器;即智能指针中实际有两个指针,故智能指针在内存中的大小时8个字节
- 一个指向资源对象本身(element_type* _Ptr{nullptr});
- 一个指向资源对象的引用计数(_Ref_count_base* _Rep{nullptr});该指针中包括连个成员变量_Uses,_Weaks
- _Uses记录了资源的引用计数,也就是引用资源的shared_ptr 的个数;
- _Weaks记录了weak_ptr的个数,相当于资源观察者的个数。
enable_shared_from_this引入
当类A被share_ptr管理时,类A的成员函数需要把当前对象作为参数传其他函数时,需要传递一个自身的share_ptr
class A
{
public:
A() :data(new int)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
delete data;
data = nullptr;
}
private:
int* data;
};
int main()
{
A* p = new A(); // 裸指针指向堆上的对象
shared_ptr<A> ptr1(p);// 用shared_ptr智能指针管理指针p指向的对象
shared_ptr<A> ptr2(p);// 用shared_ptr智能指针管理指针p指向的对象
// 下面两次打印都是1,因此同一个new A()被析构两次,逻辑错误
cout << ptr1.use_count() << endl;
cout << ptr2.use_count() << endl;
return 0;
}
class A
{
public:
A() :data(new int)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
delete data;
data = nullptr;
}
shared_ptr<A> getdate()
{
return shared_ptr<A>(this);
}
private:
int* data;
};
int main()
{
A* p = new A();
shared_ptr<A> ptr1(p);
shared_ptr<A> ptr2 = ptr1->getdate();
cout << ptr1.use_count() << endl;
cout << ptr2.use_count() << endl;
return 0;
}
上边两种代码的结果是一样的,虽然使用两个智能指针,但是他们的引用次数都为1(如下图所示),都是调用的类A的构造函数,且会释放两次,所以会导致程序崩溃;
若使用拷贝构造函数就不会出现释放两次的现象
shared_ptr<int> ptr1(new int);
shared_ptr<int> ptr2(ptr1);
cout<<ptr1.use_count()<<endl;
cout<<ptr2.use_count()<<endl;
在类中返回该类对象时,可以使用enable_shared_from_this类的shared_from_this()方法,
class A : public enable_shared_from_this<A>
{
public:
A() :data(new int)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
delete data;
data = nullptr;
}
shared_ptr<A> getdate()
{
return shared_from_this();
}
private:
int* data;
};
下面时enable_shared_from_this的部分源码
template<class _Ty>
class enable_shared_from_this { // provide member functions that create shared_ptr to this
public:
using _Esft_type = enable_shared_from_this;
_NODISCARD shared_ptr<_Ty> shared_from_this() {
return shared_ptr<_Ty>(_Wptr);
}
_NODISCARD shared_ptr<const _Ty> shared_from_this() const {
return shared_ptr<const _Ty>(_Wptr);
}
......
private:
mutable weak_ptr<_Ty> _Wptr;
};
当类A继承enable_shared_from_this后面会有一个成员变量_Wptr,当调用普通构造函数时,会有一个弱智能指针,当调用shared_from_this时,会将_Wptr这个弱智能指针类型转为强智能指针;
template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
explicit shared_ptr(const weak_ptr<_Ty2>& _Other) { // construct shared_ptr object that owns resource *_Other
if (!this->_Construct_from_weak(_Other)) {
_Throw_bad_weak_ptr();
}
}
template <class _Ty2>
bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
// if通过判断资源的引用计数是否还在,判定对象的存活状态,对象存活,提升成功;
if (_Other._Rep && _Other._Rep->_Incref_nz()) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
return true;
}
return false;
}
上述过程没有调用类A的构造函数,没有产生额外的引用计数对象,所以不会存在多次释放; 通过weak_ptr到shared_ptr的提升,还可以在多线程环境中判断对象是否存活或者已经析构释放,在多线程环境中是很安全的。
总结
-
shared_ptr
会增加资源的引用计数,常用于管理对象的生命周期。 -
weak_ptr
不会增加资源的引用计数,常作为观察者用来判断对象是否存活。 -
使用
shared_ptr
的普通拷贝构造函数会产生额外的引用计数对象,可能导致对象多次析构。使用shared_ptr
的拷贝构造函数则只影响同一资源的同一引用计数的增减。 -
当需要返回指向当前对象的
shared_ptr
时,优先使用enable_shared_from_this
机制。