当前位置: 代码迷 >> 综合 >> 智能指针 auto_ptr(C++ Primier-8)
  详细解决方案

智能指针 auto_ptr(C++ Primier-8)

热度:67   发布时间:2023-12-20 11:23:43.0

8.4.2 auto_ptr

>是C++标准库提供的类模板, 帮助自动管理用new表达式动态分配的单个对象;
Note 对new表达式分配的数组管理没有支持, 不能用auto_ptr存储数组, 结果是未定义的;

>autp_ptr对象被初始化为指向由new表达式创建的动态分配对象, 当auto_ptr对象生命期结束, 动态分配的对象被自动释放;

>头文件: #include <memory> 

>三种定义形式 1) auto_ptr<type_pointed_to> identifier (ptr_allocated_by_new); 
                     2) auto_ptr<type_pointed_to> identifier (auto_ptr_of_same_type); 
                     3) auto_ptr<type_pointed_to> identifier;
type_pointed_to代表new表达式创建的对象的类型;

>Ex. auto_ptr<int>pi (new int(1024)); pi被初始化为new表达式创建的对象的地址, 初值为1024; 操作语法不变: Ex. if (*pi == 1024) {...}

>由于auto_ptr的操作支持都是内联的, 由编译器展开, 所以使用的效率开销不会变化很大;

>auto_ptr模板支持所有权: 当一个auto_ptr对象被另一个auto_ptr对象初始化或赋值时, 左边对象拥有了空闲存储区内底层对象的所有权, 右边对象则撤销所有责任, 不再指向原对象;

>创建auto_ptr对象, 但是没有用指针初始化, Ex. auto_ptr<int> p_auto_int; p_auto_int的内部指针值被设置为0; 对这个auto_ptr解除引用会使程序出现未定义的行为, Ex. if (*p_auto_int != 1024) {...} //异常退出;

>检测一个auto_ptr对象是否指向一个底层对象的操作: 使用get()操作, Ex. if (p_auto_int.get() != 0 && ) {...} //判断auto_ptr是否指向一个对象;

>如果没有指向一个对象, 使用reset()操作重新指向, 不能直接用new表达式创建对象的地址来赋值: Ex. auto_ptr<int>pi; pi = new int(1);//错误; 但是可以用auto_ptr赋值, Ex. pi = auto_ptr<int>(new int(1));

>对于auto_ptr对象, 如果不希望设置(或取消原来的设置), 可以传入0值; Ex. ptr_auto.reset(0); 如果auto_ptr指向一个对象并且拥有该对象的所有权, 在底层指针被重置前, auto_ptr原来拥有的对象会被删除;
Note 这种情况下, 字符串操作assign()对原有字符串对象重新赋值, 比删除原有字符串对象并重新分配更有效; Ex. pstr2_auto->assign("pstr2_auto");

//case 1)
auto_ptr<string> pstr1_auto(new string("auto ptr"));
auto_ptr<string> pstr2_auto(pstr1_auto);
cout<<*pstr2_auto<<endl;
//case 2)
auto_ptr<int> pi1_auto(new int(1024));
auto_ptr<int> pi2_auto(new int(2048));
pi1_auto = pi2_auto;
cout<<*pi1_auto<<endl; 
//case 3)
auto_ptr<int> p_auto_int;
if (p_auto_int.get() != 0 && *p_auto_int != 1024)
{cout<<"existing value"<<endl;*p_auto_int = 1024;
}
else
{p_auto_int.reset(new int(512));
}
cout<<*p_auto_int<<endl;
//set empty auto_ptr
p_auto_int.reset(0);
if (p_auto_int.get() == 0)
{cout<<"reset to 0"<<endl;
}

Note 1)不能用一个指向"内存不是通过new表达式分配的"指针来初始化或赋值auto_ptr; auto_ptr在delete时, 删除不是动态分配的指针, 会导致未定义程序行为; 

int* pInt = (int*)malloc(sizeof(int));
auto_ptr<int>pInt_auto(pInt); //not safe

Note 2)不能让两个auto_ptr对象拥有空闲存储区内同一对象的所有权; Ex.用同一个指针初始化或赋值两个auto_ptr对象; 对于已有的auto_ptr, 通过get()操作把同指针赋值给另一个对象;

auto_ptr<string>pstr_auto2(pstr_auto1.get()); //bad behavior;

Note: 注意智能指针作为参数 1)按值传递 Ex. void Func(auto_ptr<string> ptr_auto_str); Func(ptr_auto_str2); Func()调用以后, 参数传入时编译器会产生临时变量接收变量, 类似拷贝构造, ptr_auto_str2会失去对象所有权, 函数退出时该对象被auto_ptr删除(auto_ptr出栈); 此时ptr_auto_str2已经为empty(00000);
2)引用传递, 没有临时拷贝; 但是函数内部可能对auto_ptr作出错误的使用(bad behavior),导致运行期错误; 所以需要加上const, Ex. void Func(const auto_ptr<string> &ptr_auto_str);

> release()操作允许将一个auto_ptr对象的底层对象初始化或赋值给第二个对象, 不会使两个auto_ptr对象同时拥有同一对象的所有权; release()不仅和get()一样返回底层对象地址, 而且释放对象所有权; Ex. auto_ptr<int> pi2_auto(pi1_auto.release());

---auto_ptr End---

OTHER

>在C++中, 应当避免把auto_ptr放入容器中, Ex. vector<auto_ptr<CLASS>> vec; //bad behavior;
当操作容器的时候, 很难避免STL内部对容器中的元素实现赋值传递, 这样会使容器中多个元素被设为NULL(unexceped);

>关键点: 1)利用"栈上对象在离开作用返回时会自动析构"的特点; 2)对动态分配的内存, 其作用范围是程序员手动控制的, 这给程序员带来方便也不可避免疏忽造成内存泄漏; 3)auto_ptr在栈上构建对象, 对象中wrap了动态分配内存的指针, 对指针的操作都转为对对象的操作; auto_ptr在析构函数中会自动释放指针的空间;

>使用auto_ptr方便内存管理的例子: 不需要在每个结束点手动添加delete;

#include <iostream>
#include <memory>
using namespace std;
class TC
{public:TC(){cout<<"TC()"<<endl;}~TC(){cout<<"~TC()"<<endl;}
};
void foo(bool isThrow)
{auto_ptr<TC> pTC(new TC);    // 方法2//TC *pTC = new TC;            // 方法1try{if(isThrow)throw "haha";}catch(const char* e){//delete pTC;           // 方法1throw;}//delete pTC;              // 方法1
}
int main()
{try{foo(true);}catch(...){cout<<"caught"<<endl;}system("pause");
}

 >源码

template<class _Ty>  struct auto_ptr_ref  {   // proxy reference for auto_ptr copying  explicit auto_ptr_ref(_Ty *_Right)  : _Ref(_Right)  {   // construct from generic pointer to auto_ptr ptr  }  _Ty *_Ref;  // generic pointer to auto_ptr ptr  };  template<class _Ty>  class auto_ptr  {   // wrap an object pointer to ensure destruction  
public:  typedef auto_ptr<_Ty> _Myt;  typedef _Ty element_type;  explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()  : _Myptr(_Ptr)  {   // construct from object pointer  }  auto_ptr(_Myt& _Right) _THROW0()  : _Myptr(_Right.release())  {   // construct by assuming pointer from _Right auto_ptr  }  auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()  {   // construct by assuming pointer from _Right auto_ptr_ref  _Ty *_Ptr = _Right._Ref;  _Right._Ref = 0;    // release old  _Myptr = _Ptr;  // reset this  }  template<class _Other>  operator auto_ptr<_Other>() _THROW0()  {   // convert to compatible auto_ptr  return (auto_ptr<_Other>(*this));  }  /  //  当auto_ptr的拷贝构造函数以及赋值操作符函数的参数是auto_ptr-  //  右值引用对象时,该参数将被转换为 auto_ptr_ref  //  编译器正是通过调用下面的隐式类型转换函数来完成这一隐式转换的  /  template<class _Other>  operator auto_ptr_ref<_Other>() _THROW0()  {   // convert to compatible auto_ptr_ref  _Other *_Cvtptr = _Myptr;   // test implicit conversion  auto_ptr_ref<_Other> _Ans(_Cvtptr);  _Myptr = 0; // pass ownership to auto_ptr_ref  return (_Ans);  }  template<class _Other>  _Myt& operator=(auto_ptr<_Other>& _Right) _THROW0()  {   // assign compatible _Right (assume pointer)  reset(_Right.release());  return (*this);  }  template<class _Other>  auto_ptr(auto_ptr<_Other>& _Right) _THROW0()  : _Myptr(_Right.release())  {   // construct by assuming pointer from _Right  }  _Myt& operator=(_Myt& _Right) _THROW0()  {   // assign compatible _Right (assume pointer)  reset(_Right.release());  return (*this);  }  _Myt& operator=(auto_ptr_ref<_Ty> _Right) _THROW0()  {   // assign compatible _Right._Ref (assume pointer)  _Ty *_Ptr = _Right._Ref;  _Right._Ref = 0;    // release old  reset(_Ptr);    // set new  return (*this);  }  ~auto_ptr()  {   // destroy the object  delete _Myptr;  }  _Ty& operator*() const _THROW0()  {   // return designated value  #if _ITERATOR_DEBUG_LEVEL == 2  if (_Myptr == 0)  _DEBUG_ERROR("auto_ptr not dereferencable");  #endif /* _ITERATOR_DEBUG_LEVEL == 2 */  return (*get());  }  _Ty *operator->() const _THROW0()  {   // return pointer to class object  #if _ITERATOR_DEBUG_LEVEL == 2  if (_Myptr == 0)  _DEBUG_ERROR("auto_ptr not dereferencable");  #endif /* _ITERATOR_DEBUG_LEVEL == 2 */  return (get());  }  _Ty *get() const _THROW0()  {   // return wrapped pointer  return (_Myptr);  }  _Ty *release() _THROW0()  {   // return wrapped pointer and give up ownership  _Ty *_Tmp = _Myptr;  _Myptr = 0;  return (_Tmp);  }  void reset(_Ty *_Ptr = 0)  {   // destroy designated object and store new pointer  if (_Ptr != _Myptr)  delete _Myptr;  _Myptr = _Ptr;  }  private:  _Ty *_Myptr;    // the wrapped object pointer  };  

<Refer to>  http://blog.csdn.net/monkey_d_meng/article/details/5901392 &  http://blog.csdn.net/btwsmile/article/details/6819900