在之前的文章中介绍了智能指针的应用场景, 并简易实现了 shared_ptr 和 unique_ptr 两种智能指针, 但是还不够全面,比如不支持注册资源释放函数、不支持多线程等。本文就来实现一个相对完整的 shared_ptr 智能指针,毕竟 unique_ptr 比它要简洁,实现了 shared_ptr 后再看 unique_ptr 就容易了。
完整代码地址:https://github.com/pengguoqing/samples_code/tree/master/c%2B%2B/shared_ptr
一、引用计数
引用计数类的实现在之前的文章中已经详细介绍了,shared_ptr 就是用它来支撑多线程的,这里直接放上链接:引用计数类实现
二、CEXSharedPtr实现细节
2.1 CEXSharedPtr 数据成员
shared_ptr 数据成员至少应该有两个,一个是指向资源的指针,另外一个指向引用计数的指针。除此之外,用户还可以自定义资源释放,所以最多三个数据成员就可以了。 C++ 标准库里面的实现只有数据指针和控制块指针, 就是它把 引用计数,析构器和弱引用封装到了一起,本文的实现就不封装了。
private:
CEXRefCounter* m_ref;
T* m_data;
Function m_deleter;
2. 2 CEXSharedPtr构造和析构函数
构造函数包括无参构造,有参构造,拷贝构造和移动构造,需要注意的地方就是注册析构器时需要用到万能引用(转发应用), 实现细如下:
inline CEXSharedPtr()
:m_ref(nullptr),
m_data(nullptr)
{}
inline CEXSharedPtr(T* ptr)
:m_ref(ptr ? new CEXRefCounter(1) : nullptr),
m_data(ptr),
m_deleter(Destutct())
{}
inline CEXSharedPtr(T* ptr, CEXRefCounter* ref)
: m_ref(ref),
m_data(ptr),
m_deleter(Destutct())
{
if (m_ref) m_ref->AddRef();
}
//注册类似 lambda表达式 的仿函数析构器
template<class T, class LambdaDestruct>
inline CEXSharedPtr(T* ptr, LambdaDestruct&& lambda)
:m_ref(ptr ? new CEXRefCounter(1) : nullptr),
m_data(ptr),
m_deleter(lambda)
{}
inline CEXSharedPtr(const CEXSharedPtr& another)
:m_data(another.m_data),
m_ref(another.m_ref),
m_deleter(another.m_deleter)
{
if (m_ref) m_ref->AddRef();
}
inline CEXSharedPtr(CEXSharedPtr&& another) noexcept
:m_data(another.m_data),
m_ref(another.m_ref),
m_deleter(another.m_deleter)
{
another.m_ref = nullptr;
another.m_data = nullptr;
}
析构函数的逻辑是将引用计数减一, 当发现引用计数为 0 时则调用析构器释放资源,提供两种析构器,分别用于释放指针和数组指针。
template<class T>
struct CXRelease
{
void operator()(T* data) noexcept
{
delete data;
}
};
template<class T>
struct CXReleaseArray
{
void operator()(T* data) noexcept
{
delete[] data;
}
};
资源释放函数为和析构函数为:
inline ~CEXSharedPtr()
{
Release();
}
private:
void Release()
{
if (m_ref && 0 >= m_ref->Release())
{
m_deleter(m_data);
m_data = nullptr;
delete m_ref;
m_ref = nullptr;
}
}
2. 3 CEXSharedPtr赋值运算符重载, 重置和赋值函数
包括移动赋值,拷贝赋值和指针赋值。
CEXSharedPtr& operator = (const CEXSharedPtr& another)
{
return Assign(another);
}
CEXSharedPtr& operator = (T* data)
{
return Assign(data);
}
CEXSharedPtr& operator = (CEXSharedPtr&& another) noexcept
{
Swap(another);
another.Reset();
return *this;
}
CEXSharedPtr& Assign(T* data)
{
if (Get() != data)
{
CEXSharedPtr tmp(data);
Swap(tmp);
}
return *this;
}
CEXSharedPtr& Assign(const CEXSharedPtr& another)
{
if (&another != this))
{
CEXSharedPtr tmp(another);
Swap(tmp);
}
return *this;
}
void Reset()
{
Assign(nullptr);
}
void Reset(T* data)
{
Assign(data);
}
void Reset(const CEXSharedPtr& another)
{
Assign(another);
}
2. 4 CEXSharedPtr指针相关运算符
CEXSharedPtr对象当指针来使用时常用的操作符重载,以及逻辑判断中的类型转换。
T* Get() const noexcept
{
return m_data;
}
T* operator -> () const noexcept
{
return m_data;
}
T& operator * () const noexcept
{
return *m_data;
}
explicit operator bool() const noexcept
{
return nullptr != m_data;
}
bool operator !() const noexcept
{
return nullptr == m_data;
}
bool operator == (const T* data) const
{
return Get() == data;
}
bool operator == (const CEXSharedPtr& another) const
{
return Get() == another->Get();
}
int Count() const
{
return m_ref ? m_ref->value() : 0;
}
2. 5 CEXSharedPtr 对象创建函数
主要主要模仿 C++ 标准库的 make_shared,实现类似的功能。
template <class T, class... Args>
CEXSharedPtr<T> MakeShared(Args&&... args)
{
return CEXSharedPtr<T>(new T{std::forward<Args>(args)...});
}
template <class T>
CEXSharedPtr<T> MakeArrayShared(int size)
{
return CEXSharedPtr<T>(new T[size], CXReleaseArray<T>());
}
三、部分测试代码
简要测试代码和运行结果如下:
CEXSharedPtr<int> ptr(new int{0});
std::cout<< "ptr" <<"count: "<<ptr.Count()<<" data: "<<*ptr<<std::endl;
CEXSharedPtr<int> ptr1(new int{1}, [](int* ptr){delete ptr;});
std::cout << "ptr1" << "count: " << ptr1.Count() << " data: " << *ptr1 << std::endl;
std::function<void(int*)> deleter(CustomDeleter);
CEXSharedPtr<int> ptr2(new int{2}, deleter);
std::cout << "ptr2" << "count: " << ptr2.Count() << " data: " << *ptr2 << std::endl;
CEXSharedPtr<int> ptr3(std::move(ptr2));
ptr2.Count();
std::cout << "ptr3" << "count: " << ptr3.Count() << " data: " << *ptr3 << std::endl;
CEXSharedPtr<int> ptr4(ptr3);
std::cout << "ptr4" << "count: " << ptr4.Count() << " data: " << *ptr4 << std::endl;
CEXSharedPtr<CXSuper> superPtr(new CXSub);
CEXSharedPtr<CXSub> subPtr(superPtr.Cast<CXSub>());
CEXSharedPtr<CXMembers> initListPtr = MakeShared<CXMembers>('I', 1, .5f);
CEXSharedPtr<CXMembers> arrayPtr = MakeArrayShared<CXMembers>(5);
std::cout << "Hello World!\n";