c++ 智能指针 (auto_ptr、unique_ptr、shared_ptr、weak_ptr)

本文详细探讨了auto_ptr、unique_ptr、shared_ptr和weak_ptr等智能指针的概念、特性和应用场景,对比了它们之间的差异,并深入分析了智能指针在解决资源管理、相互引用等问题上的优势与挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

auto_ptr(不建议使用)

  所有权唯一,即只允许一个智能指针指向同一块堆内存。如果有两个智能指针,则回收旧智能指针的所有权。
  缺点:指针的赋值或拷贝过程中,回收旧智能指针的所有权,导致旧智能指针无效,若之后给旧智能指针赋值,程序会报错。因此,不建议使用。

#include<iostream>

template<typename T>
class Auto_Ptr
{
public:
     Auto_Ptr(T* ptr)
         :mptr(ptr)
     {}
     Auto_Ptr(Auto_Ptr<T>& rhs)
     {
         mptr=rhs.Release();
     }
     Auto_Ptr<T>& operator=(Auto_Ptr<T>& rhs)
     {
         if (this != &rhs)
         {
             delete mptr;
             mptr = rhs.Release();
         }
         return *this;
     }
     T* operator->()
     {
          return mptr;
     }
     T& operator*()
     {
          return *mptr;
     }
     ~Auto_Ptr()
     {
          delete mptr;
     }
private:
     T* Release()
     {
           T* tmp = mptr;
           mptr = NULL;
           return tmp;
     }
     T* mptr;
};
int main()
{
     Auto_Ptr<int> ap1(new int);
     Auto_Ptr<int> ap2 = ap1;

     *ap2 = 30;//正确
     //*ap1 = 20;//错误,ap1已经失效
     return 0;
}

改进方法:
  带标志位的智能指针(库里没有):所有权不唯一,释放权唯一。
  缺点:释放权转移,导致智能指针提前失效。

#include<iostream>

template<typename T>
class SmartPtr
{
public:
     SmartPtr(T* ptr)
        :mptr(ptr), flag(true)
     {}
     SmartPtr(SmartPtr<T>& rhs)
     {
         mptr = rhs.mptr;
         flag = rhs.flag;
         rhs.flag = false;
     }
     SmartPtr<T>& operator=(SmartPtr<T>& rhs)
     {
          if (this != &rhs)
          {
              this->~SmartPtr();
              mptr = rhs.mptr;
              flag = rhs.flag;
              rhs.flag = false;
          }
          return *this;
     }
     ~SmartPtr()
     {
          if (flag)//有释放权
          {
              delete mptr;
          }
          mptr = NULL;
     }
     T* operator->()
     {
          return mptr;
     }
     T& operator*()
     {
          return *mptr;
     }
private:
    T* mptr;
    bool flag;//释放权的标识   true   false
};
int main()
{
    SmartPtr<int> sp1 = new int;
    SmartPtr<int> sp2 = sp1;
    *sp2 = 30;//正确
    *sp1 = 20;//正确
    return 0;
}

假设有个函数如下:

void getObject( SmartPtr<int>& arg)
{
     SmartPtr<int> tmp = arg;
 //...
}
//释放权转移,导致智能指针提前失效
int main()
{
     SmartPtr<int> sp1 = new int(10);
     getObject(sp1);
     *sp1 = 30;//程序不会报错,但存在问题
     return 0;
}

  这个函数中存在对象tmp,这个对象在函数调用完成后退栈销毁,会把堆内存释放掉的。sp1对应的堆内存已经被释放掉,当主函数再次给sp1赋值时,会有问题。但该堆内存给当前应用程序分配过,所以检测不出来。

unique_ptr

  所有权唯一,不允许权限转移。实现时将拷贝构造函数和赋值运算符重载函数写在私有下。
  缺点:通过其他手段使多个智能指针指向同一堆内存,可能会释放失败(具体如下)。

#include<iostream>

template<typename T>
class Unique_Ptr
{
public:
     Unique_Ptr(T* ptr)
        :mptr(ptr)
     {}
     ~Unique_Ptr()
     {
         delete mptr;
     }
     T* operator->()
     {
         return mptr;
     }
     T& operator*()
     {
         return *mptr;
     }
private:
     Unique_Ptr(const Unique_Ptr<T>&);
     Unique_Ptr<T>& operator=(const Unique_Ptr<T>&);
     T* mptr;
};
int main()
{
     Unique_Ptr<int> up1(new int);
     Unique_Ptr<int> up2(up1);//设计点不允许多个智能指针指向同一堆内存,错误
     return 0;
}

  我们用其他手段可以让多个智能指针指向同一堆内存(如下),程序能够编译通过,但在程序结束时,up3的销毁已经释放了堆内存,up2、up1销毁时,会导致同一块堆内存被重复释放。

int main()
{
    int*p = new int;
    Unique_Ptr<int> up1(p);
    Unique_Ptr<int> up2(p);
    Unique_Ptr<int> up3(p);
    return 0;
}
shared_ptr(常用)

  允许多个智能指针指向同一块堆内存。最后一个销毁的对象释放该堆内存。也叫做带引用计数的智能指针、强智能指针。
  缺点:相互引用的时候可能会出现问题。

#include<iostream>
class Ref_Management
{
public:
     Ref_Management()
     {
         current = 0;
     }
     void addRef(void* ptr)
     {
         int index = FindIndex(ptr);
         if (index < 0)
         {
             node[current].addr = ptr;
             node[current].count = 1;
             current++;
         }
         else
         {
             node[index].count++;
         }
     }
     void delRef(void* ptr)
     {
         int index = FindIndex(ptr);
         if (index < 0)
         {
             throw std::exception("ref is error!");
         }
         else
         {
             if (getRef(ptr) > 0)
             {
                  node[index].count--;
             }
         }
      }
      int getRef(void* ptr)
      {
          int index = FindIndex(ptr);
          if (index < 0)
          {
              return -1;
          }
          else
          {
              return node[index].count;
          }
      }
private:
      int FindIndex(void* ptr)//找地址对应的下标
      {
          for (int i = 0; i < 10; i++)
          {
               if (node[i].addr == ptr)
               {
                   return i;
               }
          }
          return -1;
     }
     class Node
     {
     public:
          Node(void* add = NULL, int cnt = 0)
            :addr(add), count(cnt)
          {}
     public:
          void* addr;
          int count;
     };
     Node node[10];//这里未考虑扩容
     int current;//有效元素个数
};

template<typename T>
class Shared_Ptr
{
public:
     Shared_Ptr(T* ptr = NULL)
        :mptr(ptr)
     {
        rm.addRef(mptr);
     }
     Shared_Ptr(const Shared_Ptr<T>& rhs)
     {
        mptr = rhs.mptr;
        rm.addRef(mptr);
     }
     Shared_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
     {
        if (this != &rhs)
        {
           rm.delRef(mptr);
           if (rm.getRef(mptr) == 0)
           {
              delete mptr;
           }
           mptr = rhs.mptr;
           rm.addRef(mptr);
        }
        return *this;
     }
     ~Shared_Ptr()
     {
        rm.delRef(mptr);
        if (rm.getRef(mptr) == 0)
        {
           delete mptr;
        }
        mptr = NULL;
     }
     T* operator->()
     {
        return mptr;
     }
     T& operator*()
     {
        return *mptr;
     }
     T* getPtr()const
     {
        return mptr;
     }
private:
     T* mptr;
     static Ref_Management rm;
};
template<typename T>
Ref_Management Shared_Ptr<T>::rm;

int main()
{
     int* p = new int;
     Shared_Ptr<int> sp1(p);
     Shared_Ptr<int> sp2(p);//正确
     Shared_Ptr<int> sp3(p);//正确,同一堆内存被多个智能指针指向

     Shared_Ptr<int> sp4(new int);
     sp2 = sp4;//正确
     return 0;
}

相互引用问题:

//存在以下两个类
class B;
class A
{
public:
     A()
     {
        std::cout << "A::A()" << std::endl;
     }
     ~A()
     {
        std::cout << "A::~A()" << std::endl;
     }
public:
     Shared_Ptr<B> spa;
};
class B
{
public:
     B()
     {
        std::cout << "B::B()" << std::endl;
     }
     ~B()
     {
        std::cout << "B::~B()" << std::endl;
     }
public:
     Shared_Ptr<A> spb;
};
int main()
{
     Shared_Ptr<A> pa = new A();
     Shared_Ptr<B> pb = new B();
     pa->spa = pb;
     pb->spb = pa;
     return 0;
}

在这里插入图片描述
相互引用之后如下:
在这里插入图片描述
程序的运行结果如下:
在这里插入图片描述
  程序结束后,根本没有调动A和B的析构函数,导致内存泄露。

weak_ptr

  主要是为了解决强智能指针(shared_ptr)相互引用的问题。不能单独使用,要与强智能指针一起使用。
  接着上述(强智能指针)程序,设计弱智能指针如下:

template<typename T>
class Weak_Ptr
{
public:
     Weak_Ptr(T* ptr = NULL)
       :mptr(ptr)
     {}
     Weak_Ptr(const Weak_Ptr<T>& rhs)
     {
        mptr = rhs.mptr;
     }
     Weak_Ptr<T>& operator=(const Weak_Ptr<T>& rhs)
     {
        if (this != &rhs)
        {
           mptr = rhs.mptr;
        }
        return *this;
     }
     Weak_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
     {
        mptr = rhs.getPtr();
        return *this;
     }
     ~Weak_Ptr()
     {}
     T* operator->()
     {
        return mptr;
     }
     T& operator*()
     {
        return *mptr;
     }
private:
     T* mptr;
};
int main()
{
    Shared_Ptr<A> pa = new A();
    Shared_Ptr<B> pb = new B();
    pa->spa = pb;
    pb->spb = pa;
    return 0;
}

  这时程序的运行结果如下,正确:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值