一、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即可。