在使用std::shared_ptr时,有两种不同的构造方式,如:
#include <iostream>
#include <memory>
int main(){
std::shared_ptr<int> sp1(new int(10));
std::shared_ptr<int> sp2 = std::make_shared<int>(10);
return 0;
}
C++11 中引入了智能指针, 同时还有一个模板函数 std::make_shared 可以返回一个指定类型的 std::shared_ptr, 那与 std::shared_ptr 的构造函数相比它能给我们带来什么好处呢
make_shared初始化的优点
1、提高性能
shared_ptr 需要维护引用计数的信息:
强引用, 用来记录当前有多少个存活的 shared_ptrs 正持有该对象. 共享的对象会在最后一个强引用离开的时候销毁( 也可能释放).
弱引用, 用来记录当前有多少个正在观察该对象的 weak_ptrs. 当最后一个弱引用离开的时候, 共享的内部信息控制块会被销毁和释放 (共享的对象也会被释放, 如果还没有释放的话).
如果你通过使用原始的 new 表达式分配对象, 然后传递给 shared_ptr (也就是使用 shared_ptr 的构造函数) 的话, shared_ptr 的实现没有办法选择, 而只能单独的分配控制块:需要开辟两块内存空间,一块内存空间 ptr 指向存储的数据;另外一块内存空间 ref 指向引用计数:强引用和弱引用;
源码:
// CLASS TEMPLATE shared_ptr
template<class _Ty>
class shared_ptr
: public _Ptr_base<_Ty>
{ // class for reference counted resource management
private:
using _Mybase = _Ptr_base<_Ty>;
......
//share_ptr 继承于_Ptr_base
// CLASS TEMPLATE _Ptr_base
template<class _Ty>
class _Ptr_base
{ // base class for shared_ptr and weak_ptr
public:
......
private:
element_type * _Ptr{nullptr};
_Ref_count_base * _Rep{nullptr};
......
}
可以看出:在share_ptr 继承于 _Ptr_base,_Ptr_base中有两个指针:
element_type * _Ptr{nullptr}; 指向管理的数据
_Ref_count_base * _Rep{nullptr};指向引用计数
// CLASS _Ref_count_base
class __declspec(novtable) _Ref_count_base
{ // common code for reference counting
private:
#ifdef _M_CEE_PURE
virtual void _Destroy() noexcept
{ // permanent workaround to avoid mentioning _purecall in msvcurt.lib, ptrustu.lib, or other support libs
_STD terminate();
}
virtual void _Delete_this() noexcept
{ // permanent workaround to avoid mentioning _purecall in msvcurt.lib, ptrustu.lib, or other support libs
_STD terminate();
}
#else /* ^^^ _M_CEE_PURE ^^^ // vvv !_M_CEE_PURE vvv */
virtual void _Destroy() noexcept = 0;
virtual void _Delete_this() noexcept = 0;
#endif /* _M_CEE_PURE */
_Atomic_counter_t _Uses;//强引用计数
_Atomic_counter_t _Weaks;//弱引用计数
protected:
_Ref_count_base()
: _Uses(1), _Weaks(1) // non-atomic initializations
{ // construct
}
如果使用std::make_shared来替换:
一次分配就足够了,内存分配效率高。这是因为std::make_shared申请一个单独的内存块来同时存放int对象和控制块。这个优化减少了程序的静态大小,因为代码只包含一次内存分配的调用,并且这会加快代码的执行速度,因为内存只分配了一次。另外,使用std::make_shared消除了一些控制块需要记录的信息,这样潜在地减少了程序的总内存占用。
同时使用make_shared可以降低内存泄漏的风险。如果不使用make_shared ,当 new int 时候成功,但是 new _Ref_count_base 失败时,share_ptr在构造时失败,无法调用析构函数,导致new int 的内存无法释放;
缺点
1 make_shared 无法自定义删除器
2 对象的内存可能无法及时回收
make_shared
只分配一次内存, 这看起来很好. 减少了内存分配的开销. 问题来了, weak_ptr
会保持控制块(强引用, 以及弱引用的信息)的生命周期, 而因此连带着保持了对象分配的内存, 只有最后一个 weak_ptr
离开作用域时, 内存才会被释放. 原本强引用减为 0 时就可以释放的内存, 现在变为了强引用, 若引用都减为 0 时才能释放, 意外的延迟了内存释放的时间. 这对于内存要求高的场景来说, 是一个需要注意的问题.
shared_ptr 和weak_ptr的实现
关于share_ptr 和weak_ptr的使用C++ 智能指针_LIJIWEI0611的博客-CSDN博客
在其中的引用计数部分,增加弱引用指针:
当强智能指针shared_ptr的引用计数为0时,析构资源
当弱智能指针weak_ptr的引用计数为0时,析构引用计数对象
#include<iostream>
#include<new>
#include<stdio.h>
#include<atomic>
using namespace std;
//仿写C++的shared_ptr和weak_ptr
//当强智能指针shared_ptr的引用计数为0时,析构资源
//当弱智能指针weak_ptr的引用计数为0时,析构引用计数对象
class Object//实例类
{
private:
int value;
public:
Object(int x = 0) :value(x) { cout << "construct object" << endl; }
~Object() { cout << "deconstruct object" << endl; }
int& Value() { return value; }
const int& Value() const { return value; }
};
template<class T>
class RefCnt//引用计数类
{
private:
T* mPtr;
std::atomic<int> mCnt_s; // shared_ptr;
std::atomic<int> mCnt_w; // weak_ptr
public:
RefCnt(T* p = nullptr) : mPtr(p)
{
if (mPtr != nullptr)
{
mCnt_s = 1;
mCnt_w = 0;
}
}
void addRef_s() { mCnt_s++; }//强智能指针引用计数
int delRef_s() { return --mCnt_s; }
int getCnt_s() const { return mCnt_s.load(); }
void addRef_w() { mCnt_w++; }//弱智能指针引用计数
int delRef_w() { return --mCnt_w; }
int getCnt_w() const { return mCnt_w; }
};
template<typename T>
struct MyDeletor//删除器类
{
void operator()(T* ptr) const
{
delete ptr;
}
};
template<class T>
class weak_ptr;//弱智能指针类型声明
template<typename T, typename Deletor = MyDeletor<T> >
class shared_ptr//强智能指针类型
{
public:
shared_ptr(T* ptr = nullptr) :mPtr(ptr), mpRefCnt(nullptr)//构造函数
{
if (mPtr != nullptr)
{
mpRefCnt = new RefCnt<T>(ptr);
}
}
~shared_ptr()//析构函数
{
if (mPtr != nullptr && 0 == mpRefCnt->delRef_s())
{
mDeletor(mPtr);
if (0 == mpRefCnt->getCnt_w())
{
delete mpRefCnt;
}
}
mPtr = nullptr;
mpRefCnt = nullptr;
}
T& operator*() const { return *mPtr; }
T* operator->() const { return mPtr; }
shared_ptr(const shared_ptr<T>& src)//拷贝构造函数
:mPtr(src.mPtr), mpRefCnt(src.mpRefCnt)
{
if (mPtr != nullptr)
{
mpRefCnt->addRef_s();
}
}
shared_ptr<T>& operator=(const shared_ptr<T>& src)//赋值重载函数
{
if (this == &src || this->mPtr == src.mPtr) return *this;
if (mPtr != nullptr && 0 == mpRefCnt->delRef_s())
{
mDeletor(mPtr);
mPtr = nullptr;
if (0 == mpRefCnt->getCnt_w())
{
delete mpRefCnt;
}
mpRefCnt = nullptr;
}
mPtr = src.mPtr;
mpRefCnt = src.mpRefCnt;
if (mPtr != nullptr)
{
mpRefCnt->addRef_s();
}
return *this;
}
shared_ptr(shared_ptr<T>&& _u)//移动构造函数
{
mPtr = _u.mPtr;
mpRefCnt = _u.mpRefCnt;
_u.mPtr = nullptr;
_u.mpRefCnt = nullptr;
}
shared_ptr<T>& operator=(shared_ptr<T>&& _u)//移动赋值函数
{
if (this == &_u) return *this;
if (this->mPtr != nullptr && 0 == mpRefCnt->delRef_s())
{
mDeletor(mPtr);
mPtr = nullptr;
if (0 == mpRefCnt->getCnt_w())
{
delete mpRefCnt;
}
mpRefCnt = nullptr;
}
mPtr = _u.mPtr;
mpRefCnt = _u.mpRefCnt;
}
int use_count() const//返回引用该资源的强智能指针的个数
{
int cnt = 0;
if (mpRefCnt != nullptr)
{
cnt = mpRefCnt->getCnt_s();
}
return cnt;
}
operator bool() const
{
return (mPtr != nullptr);
}
void reset(T* ptr)//重置函数
{
if (mPtr != nullptr && 0 == mpRefCnt->delRef_s())
{
mDeletor(mPtr);
mPtr = nullptr;
if (0 == mpRefCnt->getCnt_w())
{
delete mpRefCnt;
}
mpRefCnt = nullptr;
}
mPtr = ptr;
mpRefCnt = nullptr;
if (mPtr != nullptr)
{
mpRefCnt = new RefCnt<T>(mPtr);
}
}
void swap(shared_ptr<T>& _X)//交换函数
{
std::swap(this->mPtr, _X.mPtr);
std::swap(this->mpRefCnt, _X.mpRefCnt);
}
shared_ptr(const weak_ptr<T>& _w)//拿弱智能指针拷贝构造强智能指针
{
mPtr = _w.mPtr;
mpRefCnt = _w.mpRefCnt;
mpRefCnt->addRef_s();
}
friend class weak_ptr<T>;//友元
private:
T* mPtr;// obj , nullptr;
lzy::RefCnt<T>* mpRefCnt;
Deletor mDeletor;
};
template<class T>
class weak_ptr//弱智能指针类
{
public:
weak_ptr() :mPtr(nullptr), mpRefCnt(nullptr) {}
weak_ptr(shared_ptr<T>& s) :mPtr(s.mPtr), mpRefCnt(s.mpRefCnt)//构造函数,监视强智能指针
{
if (mpRefCnt != nullptr)
{
mpRefCnt->addRef_w();
}
}
~weak_ptr()//析构函数
{
release();
}
weak_ptr(weak_ptr<T>& w) :mPtr(w.mPtr), mpRefCnt(w.mpRefCnt)//拷贝构造函数
{
if (mpRefCnt != nullptr)
{
mpRefCnt->addRef_w();
}
}
weak_ptr<T>& operator=(const weak_ptr<T>& s)//赋值重载函数
{
release();
mPtr = s.mPtr;
mpRefCnt = s.mpRefCnt;
mpRefCnt->addRef_w();
}
shared_ptr<T> lock() const//提升方法,提升为强智能指针
{
if (expired())//如果失效
{
return nullptr;
}
return shared_ptr<T>(*this);//提升成功
}
int use_const() const//弱智能指针的引用计数
{
return mpRefCnt->getCnt_s();
}
bool expired() const//判断是否失效
{
if (mpRefCnt != nullptr)
{
return mpRefCnt->getCnt_s() == 0;
}
return true;
}
friend class shared_ptr<T>;
private:
void release()//释放函数
{
if (mpRefCnt != nullptr)
{
mpRefCnt->delRef_w();
if (mpRefCnt->getCnt_s() == 0 && 0 == mpRefCnt->getCnt_w())
{
delete mpRefCnt;
mpRefCnt = nullptr;
}
}
mPtr = nullptr;
mpRefCnt = nullptr;
}
T* mPtr;
RefCnt<T>* mpRefCnt;
};