C++智能指针之AutoPtr

在学习异常的时候,异常有一个很大的缺陷,就是异常会导致执行流乱跳.从而可能会导致某些资源没有被及时得到释放等等问题.
例如这样的问题:

  1 #include <iostream>                                                                                                    
  2 #include<vector>
  3 using namespace std;
  4 void fun()
  5 {
  6     int* arr = new int[10];
  7     cout<<"arr"<<endl;
  8     vector<int> v;
  9     v.at(0);  //at()函数在标准库处理错误的方式是抛异常,抛异常后就会跳到test()函数中,就不会执行下面的代码了,就导致了new出来的对象会存在内存泄漏
 10     delete[] arr;
 11     cout<<"释放arr"<<endl;
 12 }
 13 void test()
 14 {
 15     try
 16     {
 17         fun();        
 18     }
 19     catch(exception e)
 20     {
 21         e.what();
 22     }
 23 }
 24 int main()
 25 {
 26     test();
 27     return 0;
 28 }
上面的例子就由于抛异常而导致的执行流乱跳而内存泄漏.

那么,为了解决这个问题,就出现了RAII.

RAII:Resource Acquisition Is Initialization资源分配即初始化.定义一个类来封装资源的分配和释放.在构造函数完成资源的分配和初始化,在析构函数中完成资源的清理,从而保证资源的正确初始化和释放.

那么,上面的问题我们就可以通过封装一个类将析构和构造实现,然后在定义对象时就将其定义为封装的类的对象.

Auto_ptr.h
  1 #pragma once
  2 using namespace std;
  3     
  4 template<class T>
  5 class Auto_Ptr
  6 {   
  7     public:
  8         Auto_Ptr(T* _ptr)
  9             :ptr(_ptr)
 10         {}
 11         T& operator*()   //重载了解引用
 12         {
 13             return *ptr;
 14         }
 15         T* operator->() //重载了箭头,从而可以像指针一样使用.
 16         {
 17             return ptr;                                                                                                
 18         }
 19         ~Auto_Ptr() 
 20         {
 21             delete ptr;
 22             cout<<"释放"<<endl;
 23         }
 24     protected:
 25         T* ptr;
 26 };  

test.cpp
  1 #include <iostream>                                                                                                    
  2 #include<vector>
  3 #include"Auto_Ptr.cpp"
  4 using namespace std;
  5 
  6 struct AA
  7 {
  8     int a;
  9     int b;
 10 };
 11 void fun()
 12 {
 13     Auto_Ptr<int> arr(new int(3));
 14     *arr = 5;
 15     cout<<"arr: "<<*arr<<endl<<arr.operator*()<<endl;
 16 
 17     Auto_Ptr<AA> a(new AA);   //自定义类型的智能指针
 18     (*a).a = 10;
 19     a->b = 20;
 20     cout<<"a:"<<(*a).a<<endl;
 21     cout<<"a:"<<a.operator*().a<<endl;
 22     cout<<"b:"<<a->b<<endl;
 23     cout<<"b:"<<a.operator->()->b<<endl;
 24     vector<int> v;
 25     v.at(0);
 26     cout<<"释放arr"<<endl;
 27 }
 28 void test()
 29 {
 30     try
 31     {
 32         fun();        
 33     }
 34     catch(exception e)
 35     {
 36         e.what();
 37     }
 38 }
 39 int main()
 40 {
 41     test();
 42     return 0;
 43 }           

上面这个例子就是简单的智能指针的实现,智能指针其实是RAII的一种思想.
①可以像指针一样,然后对其进行使用
②智能,构造函数保存资源,析构函数释放资源,不会存在内存泄漏.

可是,上面的例子中存在一个很大的陷阱,当把自定义类型通过原有的对象再拷贝构造一个对象时,程序就会崩溃.因为这里的拷贝构造是浅拷贝,两个指针指向同一块内存,释放一块空间两次,就出现了bug.
解决:
方法1:深拷贝.手动实现一个拷贝构造的函数,重新开辟一段内存.这样虽然可以解决问题,可是不符合我们的需求.拷贝构造本来就是想要指向同一块内存,然后让两个指针管理同一块内存.
方法2:引用计数写时拷贝.这个方法就可以满足我们的需求.

为了解决智能指针由于拷贝构造和赋值运算符重载引起的问题:
方法1:管理权转移法(如下图所示,以及存在的问题)
这里写图片描述
实现代码如下:
模拟实现智能指针:

  1 #include <iostream>                                                                                                    
  2 using namespace std;
  3 
  4 template<class T>
  5 class Auto_Ptr
  6 {
  7     public:
  8         Auto_Ptr(T* _ptr)
  9             :ptr(_ptr)
 10         {}
 11         Auto_Ptr(Auto_Ptr<T>& p)
 12             :ptr(p.ptr)
 13         {
 14             p.ptr = NULL;
 15         }
 16         Auto_Ptr<T>& operator=(Auto_Ptr<T>& p)
 17         {
 18             if(this != &p)
 19             {
 20                 delete ptr;
 21                 ptr = p.ptr;
 22                 p.ptr = NULL;
 23             }
 24             return *this;
 25         }
 26         T& operator*()
 27         {
 28             return *ptr;
 29         }
 30         T* operator->()
 31         {
 32             return ptr;
 33         }
 34         ~Auto_Ptr()
 35         {
 36             if(ptr != NULL)
 37             {
 38                 delete ptr;
 39                 cout<<"释放"<<endl;
 40             }
 41         }
 42     protected:
 43         T* ptr;
 44 };                   

方法2:
这里写图片描述
实现的代码如下:

  1 #include <iostream>
  2 using namespace std;
  3 
  4 template<class T>
  5 class Auto_Ptr
  6 {
  7     public:
  8         Auto_Ptr(T* _ptr)
  9             :ptr(_ptr)
 10              ,owner(true)
 11         {}                                                                                                             
 12         Auto_Ptr(Auto_Ptr<T>& p)
 13             :ptr(p.ptr)
 14         {
 15             owner = true;
 16             p.owner = false;
 17         }
 18         Auto_Ptr<T>& operator=(Auto_Ptr<T>& p)
 19         {
 20             if(this != &p)
 21             {
 22                 owner = true;
 23                 p.owner = false;
 24             }
 25             return *this;
 26         }
 27         T& operator*()
 28         {
 29             return *ptr;
 30         }
 31         T* operator->()
 32         {
 33             return ptr;
 34         }
 35         ~Auto_Ptr()
 36         {
 37             if(ptr != NULL && owner == true)
 38             {
 39                 delete ptr;
 40                 cout<<"释放"<<endl;
 41             }
 42         }
 43     protected:
 44         T* ptr;
 45         bool owner;
 46 };                 

总之,智能指针存在很大的缺陷,并且解决智能指针的隐患也不能得到有效的解决.
所以尽量不要用智能指针.

猜你喜欢

转载自blog.csdn.net/yinghuhu333333/article/details/80709723