C++学习之auto_ptr智能指针

来源:互联网 发布:mac 怎么设置无线鼠标 编辑:程序博客网 时间:2024/05/01 00:28

auto_ptr的设计动机

函数的操作经常是依以下模式进行:

1.获取一些资源。

2.执行一些操作。

3.释放所获取的资源。

如果一开始获取的资源被绑定在局部对象上,当函数退出时,它们的析构函数被调用,从而自动释放这些资源。然而事情并不总是如此顺利,如果资源是以显示(explicitly)获得,而且没有被绑定在任何对象上,那就需要以显示手法释放。这种情形常常发生在指针上。

如下一个运用new和delete来产生和销毁对象的典型例子:

void f() { ClassA* ptr = new ClassA;  //create an object explicitly...  //perform some operationsdelete ptr;  //clean up (destroy the object explicitly)} 
如果在delete对象之前,函数就返回了或出现异常而退出,那么函数就根本不会调用delete语句。这很可能造成资源泄露,而防止这种资源泄露的常见办法就是捕获所有异常,但这会使程序代码变得非常复杂和累赘。

如果使用智能指针,情形就会大不一样。这个智能指针应该保证,无论在何种情形下,只要自己被摧毁,就一定连带释放其所指资源。而由于智能指针本身就是区域变量,所以无论是正常退出,还是异常退出,它一定会被销毁。auto_ptr正是这种指针。

auto_ptr是这样一种指针:它是“它所指对象”的拥有者。所以,当身为对象拥有者的auto_ptr被摧毁时,该对象也将遭到摧毁。auto_ptr要求一个对象只能有一个拥有者,严禁一物二主。

#include <memory> void f() { //create and initialize anauto_ptr std::auto_ptr<ClassA> ptr(new ClassA); ...  //perform some operations}
通过使用智能指针,不再需要delete,也不需要捕获异常。不过注意:auto_ptr<>不允许使用一般指针惯用的赋值初始化方式。你必须直接使用数值来完成初始化:

std::auto_ptr<ClassA> ptr1(new ClassA);  //OKstd::auto_ptr<ClassA> ptr2 = new ClassA;  //ERROR
auto_ptr拥有权的转移
auto_ptr所界定的是一种严格的拥有权观念。也就是说,由于一个auto_ptr会删除其所指对象,所以这个对象绝对不能同时被其他对象“拥有”。绝对不应该出现多个auto_ptr同时拥有一个对象的情况。

这个条件会使auto_ptr的copy构造函数和assignment操作符的实现有所不同:copy构造函数和assignment操作符会将对象拥有权交出去。

//initialize anauto_ptr with a new objectstd::auto_ptr<ClassA> ptr1(new ClassA); //copy theauto_ptr //- transfers ownership fromptr1 toptr2 std::auto_ptr<ClassA> ptr2(ptr1); 
此时ptr2指向原ptr1所指对象,而ptr1现在指向NULL。而且要注意:只有auto_ptr可以拿来当做另一个auto_ptr的初值,普通指针是不行的:

std::auto_ptr<ClassA> ptr;    //create anauto_ptr ptr = new ClassA;    //ERRORptr = std::auto_ptr<ClassA>(new ClassA);  //OK, delete old object  // and own new
拥有权的转移,使得auto_ptr产生一种特殊用法:某个函数可以利用auto_ptr将拥有权转交给另一个函数。这种事情可能发生在两种情形下:

  1. 某函数是数据的终点。如果auto_ptr以传值方式被当做一个参数传递给某函数,就有这种情况。此时被调用端的参数获得这个auto_ptr的拥有权,如果函数不再将它传递出去,它所指的对象就会在函数退出时被删除。
  2. 某函数是数据的起点。当一个auto_ptr被返回,其拥有权便被转交给调用者了。
auto_ptr的语义本身就包含了拥有权,所以如果你无意转交你的拥有权,就不要在参数列中使用auto_ptr,也不要以它作为返回值。不过你可以运用constant reference,向函数传递拥有权。这里的constant并非意味着你不能更改auto_ptr所拥有的对象,而是你不能更改auto_ptr的拥有权。

auto_ptr作为成员之一
如果你以auto_ptr而非一般指针作为成员,当对象被删除时,auto_ptr会自动删除其所指的成员对象,于是你也就不再需要析构函数了。此外,即使在初始化期间抛出异常,auto_ptr也可以帮助避免资源泄漏。注意,只有当对象被完全构造成功,才有可能于将来调用其析构函数。这就造成了资源泄漏的隐患:如果第一个new成功了,第二个new却失败了,就会造成资源泄漏。例如:

class ClassB { private: ClassA* ptr1;  //pointer membersClassA* ptr2; public: //constructor that initializes the pointers//- will cause resource leak if secondnew throwsClassB (ClassA val1, ClassA val2) : ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) { } //copy constructor//- might cause resource leak if second new throwsClassB (const ClassB& x) : ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) { } //assignment operatorconst ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; } ~ClassB () { delete ptr1; delete ptr2; } ... }; 
使用auto_ptr,你可以轻松避免这场悲剧:

class ClassB { private: const std::auto_ptr<ClassA> ptr1;  //auto_ptr membersconst std::auto_ptr<ClassA> ptr2; public: //constructor that initializes theauto_ptrs//- no resource leak possibleClassB (ClassA val1, ClassA val2) : ptr1 (new ClassA(val1)), ptr2(new ClassA(val2)) { } //copy constructor//- no resource leak possibleClassB (const ClassB& x) : ptr1(new ClassA(*x.ptr1), ptr2(new ClassA(*x.ptr2)) { } //assignment operatorconst ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; } //no destructor necessary//(default destructor letsptr1 andptr2 delete their objects)... }; 
auto_ptr的错误运用

(1)auto_ptr之间不能共享使用权

一个auto_ptr千万不能指向另一个auto_ptr(或其他对象)所拥有的对象。否则,当第一个指针删除该对象后,另一个指针突然间指向了一个已被销毁的对象,那么,如果再使用那个指针进行读写操作,就会引发一场灾难。

(2)并不存在针对array而设计的auto_ptr

auto_ptr不可以指向array,因为auto_ptr是透过delete而非delete[]来释放其所拥有的对象。

(3)auto_ptr绝非一个“四海通用”的智能指针

并非任何使用智能指针的地方,都适用auto_ptr。特别注意,它不是引用计数型指针——这种指针保证,如果有一组智能型指针指向一个对象,那么当且仅当最后一个智能指针被销毁时,该对象才会被销毁。

(4)auto_ptr不满足STL容器对其元素的要求

auto_ptr并不满足STL容器对于元素的最基本要求,因为在拷贝和赋值动作之后,原本的auto_ptr和新产生的auto_ptr并不相等。

auto_ptr类别的实作示范

// util/autoptr.hpp/* classauto_ptr *- improved standard conforming implementation */namespace std { //auxiliary type to enable copies and assignments (now global)template<class Y> struct auto_ptr_ref { Y* yp; auto_ptr_ref (Y* rhs) : yp(rhs) { } }; template<class T> class auto_ptr { private: T* ap;  //refers to the actual owned object (if any)public: typedef T element_type; //constructorexplicit auto_ptr (T* ptr = 0) throw() : ap(ptr) { } //copy constructors (with implicit conversion)//- note: nonconstant parameterauto_ptr (auto_ptr& rhs) throw() : ap (rhs. release()) { } template<class Y> auto_ptr (auto_ptr<Y>& rhs) throw() : ap(rhs.release()) { } //assignments (with implicit conversion)//- note: nonconstant parameterauto_ptr& operator= (auto_ptr& rhs) throw() { reset(rhs.release()); return *this; } template<class Y> auto_ptr& operator= (auto_ptr<Y>& rhs) throw() { reset(rhs.release()); return *this; } //destructor~auto_ptr() throw() { delete ap; } //value accessT* get() const throw() { return ap; } T& operator*() const throw() { return *ap; } T* operator->() const throw() { return ap; } //release ownershipT* release() throw() { T* tmp(ap); ap = 0; return tmp; } //reset value void reset (T* ptr=0) throw(){ if (ap != ptr) { delete ap; ap = ptr; } } /* special conversions with auxiliary type to enable copies and assignments*/auto_ptr(auto_ptr_ref<T> rhs) throw() : ap(rhs.yp) { } auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() { //newreset(r.yp); return *this; } template<class Y> operator auto_ptr_ref<Y>() throw() { return auto_ptr_ref<Y>(release()); } template<class Y> operator auto_ptr<Y>() throw() { return auto_ptr<Y>(release()); } }; } 






0 0
原创粉丝点击