C++ 智能指针简单实现以及循环引用问题

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

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注