C++ 共享智能指针实现(支持多线程安全)
主要实现思想是引用计数法,在SharePtr类拷贝(Copy)和赋值(=)的时候引用计数加1,而在SharePtr类变量析构的时候引用计数减少1
实现细节
(1)SharePtr包裹类变量指针和引用计数指针int*, 这里引用计数采用 int* 为了在各个SharePtr变量实现计数共享,每个变量的计数变化都会影响其他计数
enum EThreadType
{
SingleThread,
MultiThread,
};
template<typename ObjectType, EThreadType ThreadType = EThreadType::SingleThread>
class SharePtr
{
private:
ObjectType* objectPtr = nullptr;
int* count = nullptr;;
mutex threadMutex;
(2) 拷贝构造和赋值引用计数加1
//拷贝构造,相当于SharePtr<ObjectType>(const SharePtr<ObjectType>& other)
SharePtr(const SharePtr& other)
{
Assignment(other);
}
//赋值构造
SharePtr& operator=(const SharePtr& other)
{
Assignment(other);
return *this;
}
void Assignment(const SharePtr<ObjectType, ThreadType>& other)
{
Decrease();
count = other.count;
objectPtr = other.objectPtr;
if (EThreadType::MultiThread == ThreadType)
threadMutex.lock();
if (count != nullptr)
{
(*count)++;
}
if (EThreadType::MultiThread == ThreadType)
threadMutex.unlock();
}
(3)析构引用计数减少1,当减少为0,则析构共享指针保存的对象的内存
void Decrease()
{
if (EThreadType::MultiThread == ThreadType)
threadMutex.lock();
if (nullptr != count)
{
(*count) -= 1;
if (*count == 0)
{
if (nullptr != objectPtr)
{
delete objectPtr;
objectPtr = nullptr;
}
delete count;
count = nullptr;
}
}
if (EThreadType::MultiThread == ThreadType)
threadMutex.unlock();
}
~SharePtr()
{
Decrease();
}
(4)共享指针SharePtr总体实现,设计了MakeShared模板函数实现了不定形参,支持模板类对象的构造函数不定形参,和C++ STL的make_shared具备相同的效果
enum EThreadType
{
SingleThread,
MultiThread,
};
template<typename ObjectType, EThreadType ThreadType = EThreadType::SingleThread>
class SharePtr
{
private:
ObjectType* objectPtr = nullptr;
int* count = nullptr;;
mutex threadMutex;
public:
SharePtr()
{
}
//拷贝构造,相当于SharePtr<ObjectType>(const SharePtr<ObjectType>& other)
SharePtr(const SharePtr& other)
{
Assignment(other);
}
//赋值构造
SharePtr& operator=(const SharePtr& other)
{
Assignment(other);
return *this;
}
ObjectType* operator->()
{
return objectPtr;
}
~SharePtr()
{
Decrease();
}
private:
void Assignment(const SharePtr<ObjectType, ThreadType>& other)
{
Decrease();
count = other.count;
objectPtr = other.objectPtr;
if (EThreadType::MultiThread == ThreadType)
threadMutex.lock();
if (count != nullptr)
{
(*count)++;
}
if (EThreadType::MultiThread == ThreadType)
threadMutex.unlock();
}
void Decrease()
{
if (EThreadType::MultiThread == ThreadType)
threadMutex.lock();
if (nullptr != count)
{
(*count) -= 1;
if (*count == 0)
{
if (nullptr != objectPtr)
{
delete objectPtr;
objectPtr = nullptr;
}
delete count;
count = nullptr;
}
}
if (EThreadType::MultiThread == ThreadType)
threadMutex.unlock();
}
public:
template<typename...Args>
void InitObject(Args&&...args)
{
if (nullptr != objectPtr)
{
delete objectPtr;
objectPtr = nullptr;
}
if (nullptr != count)
{
delete count;
count = nullptr;
}
objectPtr = new ObjectType(std::forward<Args>(args)...);
count = new int;
*count = 1;
}
template<typename ObjectType, EThreadType ThreadType,typename...Args>
friend SharePtr<ObjectType, ThreadType> MakeShared(Args&&...args);
};
//发生拷贝构造在析构
template<typename ObjectType, EThreadType ThreadType = EThreadType::SingleThread, typename...Args>
SharePtr<ObjectType, ThreadType> MakeShared(Args&&...args)
{
SharePtr<ObjectType, ThreadType> ObjectPtr;
ObjectPtr.InitObject(std::forward<Args>(args)...);
return ObjectPtr;
}
(5)SharePtr不提供直接New构造对象的函数,是考虑到智能指针的滥用,二次GC导致Delete空指针而程序崩溃, 仅仅开放出MakeShared接口来返回共享指针对象。 下面的用STL的共享指针shared_ptr滥用导致程序崩溃
class UObject
{
public:
UObject(float inValue)
{
value = inValue;
std::cout << "UObject Construct" << std::endl;
}
~UObject()
{
std::cout << "UObject Unonstruct" << std::endl;
}
float GetValue()
{
return value;
}
private:
float value;
};
void Func()
{
UObject* a = new UObject(0.6666);
shared_ptr<UObject> MyObjectPtr(a);
shared_ptr<UObject> MyObjectPtr1(a);
}
int main()
{
Func();
system("pause");
}
正确使用样例:
void Func()
{
SharePtr<UObject> MyObjectPtr = MakeShared<UObject>(0.6666f);
SharePtr<UObject> MyObjectPtr1 = MyObjectPtr;
SharePtr<UObject> MyObjectPtr2;
MyObjectPtr2 = MyObjectPtr1;
std::cout << "value = " << MyObjectPtr2->GetValue() << std::endl;
}
int main()
{
Func();
system("pause");
}
(6)因为是玩具版共享指针,没有像STL的共享指针一样使用atomic计数器,更无法解决共享指针的多线程安全问题