1.auto_ptr,share_ptr概述
在开发过程中,曾经使用过两种C++的智能指针:auto_ptr和shared_ptr如今,便总结一下,顺便比较比较二者使用中的区别,注意避免入坑的危险。
我们知道,在C++中,如果创建一个指向某个对象的指针,那么在使用完这个对象之后我们需要自己delete它。否则,会造成一个悬垂指针(dangling pointer),非常容易造成内存泄漏。事实上,如果在使用指针的同时,程序运行抛出异常,那么所指向的对象仍然不会被安全删除,依然可能会出现内存泄露的危险。与普通的指针相比而言,智能指针的区别在于它实际上是对普通指针加了一层封装机制,这样的一层封装机制的目的是使智能指针可以方便的管理一个对象的生命期。在生命期结束的时候,自动的删除内存中被占用的区域。也就是说,智能指针的出现实际上就是为了可以方便的控制对象的生命期,在智能指针中,一个对象什么时候和在什么条件下要被析构或者是删除是受智能指针本身决定的,用户并不需要管理,以此来增强程序的健壮性。
A.auto_ptr
它是C++标准库提供的一种智能指针,在构造时获取某个对象的所有权,在析构时,释放该对象RAII(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。
简单的说,RAII的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。
如下例所示,即为auto_ptr的使用规则:
int *p = new int(10);
std::auto_ptr <int> ap(p);
但是使用auto_ptr,有一些必须注意的问题:
- (1)不可以将两个或两个以上的auto_ptr指向同一个对象,因为其中当其中一个auto_ptr的生命期结束时,它就会析构对象,而另一个auto_ptr在析构时,将造成多重析构问题,这是很不安全的内存操作。故而,以下的行为是一定要避免的:
int *p = new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);
//ap1和ap2都指向一个对象,这就是极危险的,必须防止这么使用
- (2)不可使用auto_ptr来管理数组指针
因为auto_ptr在析构时,使用的是delete,而不是delete[]。所以以下的使用方式是绝对要避免的。
int *pa = new int[10];
auto_ptr<int> ap(pa);
//这种写法是完全错误的,极可能引起内存泄露的危险
- (3)
auto_ptr强调对“裸”指针的完全占有性。也就是说,一个“裸”指针不能同时被两个以上的“裸”指针所拥有。那么,在拷贝构造函数和赋值中,auto_ptr均采取了“所有权转移”的策略,即原指针将失去对裸指针的所有权。
int *p = new int (10);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2 = ap1; //这会使得ap1变成了NULL
cout << *ap1 <<endl; //错误!此时,对其解引用是不安全的。
如上的错误相对而言,比较容易避免,但是对于以下的代码,错误就较难发现。
void fun(auto_ptr<int> ap)
{
cout << *ap << endl;
}
auto_ptr<int> 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用法要点:
A. 需要包含头文件<memory>
B.Constructor:explicit auto_ptr(X* p = 0) throw(); 将指针p交给auto_ptr对象托管。
C.Copy constructor:auto_ptr(constauto_ptr&) throw(); template<class Y> auto_ptr(constauto_ptr<Y>& a) throw(); 指针的托管权会发生转移。
D.Destructor: ~auto_ptr(); 释放指针p指向的空间。
E. 提供了两个成员函数 Xget() const throw(); //返回保存的指针
F. 对象中仍保留指针 Xrelease() const throw(); //返回保存的指针,对象中不保留指针auto_ptr实现关键点:
A.利用特点“栈上对象在离开作用范围时会自动析构”。
B.对于动态分配的内存,其作用范围是程序员手动控制的,这给程序员带来了方便但也不可避免疏忽造成的内存泄漏,毕竟只有编译器是最可靠的。
C.auto_ptr通过在栈上构建一个对象a,对象a中wrap了动态分配内存的指针p,所有对指针p的操作都转为对对象a的操作。而在a的析构函数中会自动释放p的空间,而该析构函数是编译器自动调用的,无需程序员操心。
B.shared_ptr
鉴于auto_ptr所出现的无法复制,且不能满足标准容器对元素的要求,所以boost库中提供了一种新型的智能指针shared_ptr,它通过引用计数(reference counting)的原理,解决了多个指针之间共享对象所有权的问题,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。 shared_ptr也可以安全地放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。
在C++11中,shared_ptr在<memory>中被定义为: template<class T > class shared_ptr;
std::shared_ptr是通过指针保持某个对象的共享拥有权的智能指针。若干个shared_ptr对象可以拥有同一个对象;最后一个指向该对象的shared_ptr被销毁或重置时,该对象被销毁。销毁该对象时使用的是delete表达式或者是在构造shared_ptr时传入的自定义删除器(deleter)。
shared_ptr也可以不拥有对象,称作空(empt))。 shared_ptr满足CopyConstructible和CopyAssignable的要求。
- shared_ptr实现细节
成员类型 | 定义 |
---|---|
element_type T | The type of the managed object 即被shared_ptr管理着的对象 |
- 包含的成员函数如下所示:
成员函数 | 成员函数 |
---|---|
(constructor) | constructsnew shared_ptr |
(destructor) | 如果没有更多shared_ptrs的的链接解构拥有的对象 |
operator= | 分配shared_ptr |
reset | 取代管理的对象 |
swap | 交换所管理的对象 |
get | 返回一个指针,指向被管理对象 |
operator* /operator-> | 解引用指针到的管理对象 |
use_count | shared_ptr对象指的是在同一个管理对象的数量 |
unique | 检查是否被管理对象的管理仅由当前shared_ptr的实例 |
operator bool | 检查是否有相关的管理对象 |
owner_before | Owner-based ordering |
智能指针share_ptr的实现基于引用计数实现,有机会分析一个引用计数的原理。