我所熟悉的C++智能指针auto_ptr vs shared_ptr (一)

在开发过程中,曾经使用过两种C++的智能指针:auto_ptrshared_ptr,如今,便总结一下,顺便比较比较二者使用中的区别,注意避免入坑的危险。:-D

我们知道,在C++中,如果创建一个指向某个对象的指针,那么在使用完这个对象之后我们需要自己delete它。否则,会造成一个悬垂指针(dangling pointer),非常容易造成内存泄漏。事实上,如果在使用指针的同时,程序运行抛出异常,那么所指向的对象仍然不会被安全删除,依然可能会出现内存泄露的危险。
与普通的指针相比而言,智能指针的区别在于它实际上是对普通指针加了一层封装机制,这样的一层封装机制的目的是使智能指针可以方便的管理一个对象的生命期。在生命期结束的时候,自动的删除内存中被占用的区域。也就是说,智能指针的出现实际上就是为了可以方便的控制对象的生命期,在智能指针中,一个对象什么时候和在什么条件下要被析构或者是删除是受智能指针本身决定的,用户并不需要管理,以此来增强程序的健壮性。

(一)auto_ptr

它是C++标准库提供的一种智能指针,在构造时获取某个对象的所有权,在析构时,释放该对象。

RAII(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,

是C++语言的一种管理资源、避免泄漏的惯用法。

C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。

简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,

在对象生命期控制对资源的访问使之始终保持有效,

最后在对象析构的时候释放资源

如下例所示,即为auto_ptr的使用规则:

int *p = new int(10);
std::auto_ptr  ap(p);
但是使用auto_ptr,有一些必须注意的问题:

(1)不可以将两个或两个以上的auto_ptr指向同一个对象,因为其中当其中一个auto_ptr的生命期结束时,它就会析构对象,而另一个auto_ptr在析构时,将造成多重析构问题,这是很不安全的内存操作。故而,以下的行为是一定要避免的:

int *p = new int(0);
auto_ptr ap1(p);
auto_ptr ap2(p);
//ap1和ap2都指向一个对象,这就是极危险的,必须防止这么使用

(2)不可使用auto_ptr来管理数组指针。

因为auto_ptr在析构时,使用的是delete,而不是delete[]。所以以下的使用方式是绝对要避免的。

int *pa = new int[10];
auto_ptr ap(pa);
//这种写法是完全错误的,极可能引起内存泄露的危险
(3)auto_ptr强调对“裸”指针的完全占有性。也就是说,一个“裸”指针不能同时被两个以上的“裸”指针所拥有。那么,在拷贝构造函数和赋值中,auto_ptr均采取了“所有权转移”的策略,即原指针将失去对裸指针的所有权。

int *p = new int(10);
auto_ptr ap1(p);
auto_ptr ap2 = ap1; //这会使得ap1变成了NULL
cout << *ap1 <

如上的错误相对而言,比较容易避免,但是对于以下的代码,错误就较难发现。

void fun(auto_ptr ap)
{
     cout << *ap << endl;
}
auto_ptr ap(new int(0) ) ;
fun(ap);
cout << *ap << endl;  //错误!对空指针的解引用

上述情况比较隐蔽,这是因为在函数调用时,传参引起了拷贝构造函数的调用,所以原来的指针失去了其所有权。

(4)auto_ptr不具备值语义,所以auto_ptr不能被用在STL容器中。

所谓值语义:使之符合以下条件的类型,设有类 Obj:

Obj a;
Obj b(a);
Obj c;
c = a;
那么a == b, a == c

显然,auto_ptr并不具有这个特点。


auto_ptr用法要点:

1. 需要包含头文件

2. Constructor:explicit auto_ptr(X* p = 0) throw(); 将指针p交给auto_ptr对象托管。

3. Copy constructor:auto_ptr(const auto_ptr&) throw(); template auto_ptr(const auto_ptr& a) throw(); 指针的托管权会发生转移。

4. Destructor: ~auto_ptr(); 释放指针p指向的空间。

5. 提供了两个成员函数 X* get() const throw();       //返回保存的指针

6. 对象中仍保留指针 X* release() const throw();     //返回保存的指针,对象中不保留指针

 

auto_ptr实现关键点:

1. 利用特点“栈上对象在离开作用范围时会自动析构”。

2. 对于动态分配的内存,其作用范围是程序员手动控制的,这给程序员带来了方便但也不可避免疏忽造成的内存泄漏,毕竟只有编译器是最可靠的。

3. auto_ptr通过在栈上构建一个对象a,对象a中wrap了动态分配内存的指针p,所有对指针p的操作都转为对对象a的操作。而在a的析构函数中会自动释放p的空间,而该析构函数是编译器自动调用的,无需程序员操心。


后面的两点引用于http://blog.csdn.net/monkey_d_meng/article/details/5901392,谢谢作者的耐心讲解!

你可能感兴趣的:(C++语言基础知识)