一、share_ptr简单实现
template<typename T>
class SharePtr
{
T* ptr;
int* ref_count;
public:
SharePtr():ptr(nullptr), ref_count(nullptr) {} //默认构造
SharePtr(T* p):ptr(p), ref_count(new int(1)) {} //带参构造
SharePtr(SharePtr& other) :ptr(other.ptr), ref_count(&(++(*other.ref_count))) {} //拷贝构造
SharePtr& operator=(SharePtr& other)//等号赋值
{
if (&other == this)
return *this;
if (ptr)
{
if (--(*ref_count) == 0)
{
delete ref_count;
delete ptr;
}
}
ptr = other.ptr;
if (other.ref_count)
ref_count = &(++(other.ref_count));
else
ref_count = nullptr;
return *this;
}
T& operator*()
{
if (ptr)
return *ptr;
else//如果是空指针就报错
{
throw "Nullptr Error";
exit(-1);
}
}
T* operator->()
{
if (ptr)
return ptr;
else//如果是空指针就报错
{
throw "Nullptr Error";
exit(-1);
}
}
~SharePtr()
{
if (ptr && --(*ref_count) == 0)//如果计数减为0就析构
{
delete ptr;
delete ref_count;
}
}
};
二、unique_ptr简单实现
template<typename T>
class UniquePtr
{
T* ptr;
public:
UniquePtr():ptr(nullptr) {}
UniquePtr(T* p):ptr(p) {}
UniquePtr(const UniquePtr& other) = delete;//避免编译器自动生成拷贝构造
UniquePtr operator=(const UniquePtr& other) = delete;//避免编译器自动生成等号赋值
UniquePtr(UniquePtr&& other) noexcept//只允许使用移动构造
{
ptr = other.ptr;
other.ptr = nullptr;
}
UniquePtr& operator=(UniquePtr&& other) noexcept//只允许使用等号实现移动
{
if (ptr)
delete ptr;
ptr = other.ptr;
other.ptr = nullptr;
return *this;
}
T& operator*()
{
if (ptr)
return *ptr;
else
{
throw "Nullptr Error";
exit(-1);
}
}
T* operator->()
{
if (ptr)
return ptr;
else
{
throw "Nullptr Error";
exit(-1);
}
}
~UniquePtr()
{
if (ptr)
delete ptr;
}
};
三、循环引用问题
class B_class; // 前置声明
class A_class {
public:
shared_ptr<B_class> ptr;
};
class B_class {
public:
shared_ptr<A_class> ptr;
};
int main()
{
{//在一个作用域内
shared_ptr<A_class> A(new A_class());//智能指针A指向了A类对象
shared_ptr<B_class> B(new B_class());//智能指针B指向了B类对象
A->ptr = B;//智能指针A指向的对象中的一个ptr成员也是智能指针,并且指向B所指向的对象(即B类对象)
B->ptr = A;//智能指针B指向的对象中的一个ptr成员也是智能指针,并且指向A所指向的对象(即A类对象)
//所以现在A类对象和B类对象都分别有两个share_ptr
}
/*
当离开作用域是出现这种情况:
首先是智能指针A执行析构函数,因为指针A指向的对象的引用计数减去1后,还剩1,所以没有执行进一步操作而直接返回,不释放内存。
然后智能指针B执行析构函数也同样如此。
所以到最后A类对象和B类对象的内存还是没有释放。
*/
return 0;
}
四、解决方案
使用弱引用weak_ptr可解决循环引用的问题,因为weak_ptr并不会使计数器加1。按上边的例子就是说把A类和B类中的任意一个智能指针换成weak_ptr即可。