1. 代码实现
智能指针核心逻辑:
构造函数或者赋值时,增加或调整引用计数 析构函数时,减小引用计数,如果引用计数为0,就释放内存。 麻烦的地方是使用场景以及各种参数类型,要考虑全面。
这里仅考虑单线程场景,多线程使用需要再完善。
#pragma once
#define SAFE_DELETE(pObj) \
{ \
if (pObj) { \
delete pObj; \
pObj = NULL; \
} \
}
namespace Yu {
template <class T> class SmartPtr {
public:
/* 指针作为构造函数参数
* 例如:
* int* n2 = new int(6);
* Yu::SmartPtr<int> p4(n2);
*/
explicit SmartPtr(T* pObj = NULL) : m_pObj(pObj), m_pRef(new int(1)) {}
// 析构函数调用ReleaseRef减少引用计数
// 引用计数为0时,会自动释放指针
~SmartPtr()
{
ReleaseRef();
}
/* 拷贝构造函数
* 例如:
* Yu::SmartPtr<int> p1;
* Yu::SmartPtr<int> p2(p1);
*/
SmartPtr(const SmartPtr& r) : m_pObj(r.m_pObj), m_pRef(r.m_pRef)
{
AddRef();
}
/* =操作符重载
* 例如:
* Yu::SmartPtr<int> p3;
* p3 = p2;
*/
SmartPtr& operator=(const SmartPtr& r)
{
// 赋值语句左右两边是不同对象,才需要处理
if (this != &r) {
// 首先释放对原来指针的引用
ReleaseRef();
m_pRef = r.m_pRef;
m_pObj = r.m_pObj;
AddRef();
}
return *this;
}
/* =操作符重载
* 例如:
* int* n1 = new int(5);
* Yu::SmartPtr<int> p1;
* p1 = n1;
*/
SmartPtr& operator=(T* r)
{
// 首先释放对原来指针的引用
ReleaseRef();
// 已经管理一个新的指针
// 引用计数应该重新new一个,不要再使用之前的。
m_pRef = new int(1);
m_pObj = r;
return *this;
}
/* ->操作符重载
* 例如:
* auto arr = new std::vector<int>(10);
* Yu::SmartPtr<std::vector<int>> p1(arr);
* std::cout << p1->size() << std::endl;
*/
T* operator->() const
{
return m_pObj;
}
/* *操作符重载
* 例如:
* int* n1 = new int(1);
* Yu::SmartPtr<int> p1(n1);
* *p1 = 2;
*/
T& operator*() const
{
return *m_pObj;
}
/* !操作符重载
* 支持bool语境判断指针是否为空
* 例如:
* Yu::SmartPtr<int> p1;
* if (!p1) {
* XXXX;
* }
*/
bool operator!() const
{
return !m_pObj;
}
/* ==操作符重载
* 例如:
* Yu::SmartPtr<int> p1;
* Yu::SmartPtr<int> p2;
* if (p1 == p2) {
* XXXX;
* }
*/
bool operator==(const SmartPtr<T>& r) const
{
return m_pObj == r.m_pObj;
}
/* !=操作符重载
* 例如:
* Yu::SmartPtr<int> p1;
* Yu::SmartPtr<int> p2;
* if (p1 != p2) {
* XXXX;
* }
*/
bool operator!=(const SmartPtr<T>& r) const
{
return m_pObj != r.m_pObj;
}
/* 支持bool语境判断指针是否为非空
* 例如:
* Yu::SmartPtr<int> p1;
* if (p1) {
* XXXX;
* }
*/
typedef T* SmartPtr<T>::*unspecified_bool_type;
operator unspecified_bool_type() const
{
return !m_pObj ? 0 : &SmartPtr<T>::m_pObj;
}
// 获取原始指针
T* Get() const
{
return m_pObj;
}
// 获取当前指针引用计数
int GetRef() const
{
return *m_pRef;
}
private:
// 增加指针引用计数
void AddRef()
{
++*m_pRef;
}
// 减小指针引用计数
// 如果引用计数为0,释放指针
virtual void ReleaseRef()
{
--*m_pRef;
if (*m_pRef <= 0) {
SAFE_DELETE(m_pRef);
SAFE_DELETE(m_pObj);
}
}
private:
T* m_pObj;
// 多个SmartPtr要共享引用计数,所以引用计数也要用指针
int* m_pRef;
};
} // namespace Yu
代码开源地址:https://gitee.com/ferrisyu/CodingSea/blob/master/Common/smart_ptr.h
2. gtest用例
TEST_F(TestSmartPtr, test0)
{
Yu::SmartPtr<int> p1;
EXPECT_EQ(p1.Get(), nullptr);
EXPECT_EQ(p1.GetRef(), 1);
int* n1 = new int(5);
// T*赋值运算符
p1 = n1;
// 拷贝构造函数
Yu::SmartPtr<int> p2(p1);
Yu::SmartPtr<int> p3;
// SmartPtr赋值运算符
p3 = p2;
// 此时p1, p2, p3 ---> n1
EXPECT_EQ(p3.Get(), n1);
EXPECT_EQ(p3.GetRef(), 3);
// *运算符
EXPECT_EQ(*p3, *n1);
// !运算符
EXPECT_EQ(!p3, false);
// unspecified_bool_type运算符
bool isNull = true;
if (p3) {
isNull = false;
}
EXPECT_EQ(isNull, false);
// ==运算符
EXPECT_EQ(p1 == p3, true);
// T*构造函数
int* n2 = new int(6);
Yu::SmartPtr<int> p4(n2);
p3 = p4;
p4 = nullptr;
// 此时p1, p2 ---> n1, p3 ---> n2, p4 ---> nullptr
EXPECT_EQ(p2.GetRef(), 2);
EXPECT_EQ(*p2, *n1);
EXPECT_EQ(p3.GetRef(), 1);
EXPECT_EQ(*p3, *n2);
EXPECT_EQ(p4, nullptr);
}
代码开源地址:https://gitee.com/ferrisyu/CodingSea/blob/master/Test/TestCommon/test_smartptr.cpp