【C++】智能指针
auto_ptr
auto_ptr是当前C++标准库中提供的一种智能指针。
auto_ptr在构造时获取某个对象的所有去(ownership),在析构时释放该对象。我们可以这样使用auto_ptr来提高代码安全性:
int* p = new int(0); auto_ptr<int> ap(p);
从此我们不必关心应该何时释放p,也不必担心发生异常会有内存泄漏,这是因为auto_ptr的析构函数会执行指针的释放,而析构函数会在ap除了作用域以后执行。
auto_ptr的出现,主要是为了解决“被异常抛出时发生资源泄漏”的问题。即如果我们让资源在局部对象构造时分配,在局部对象析构时释放,这样即使在函数执行过程时发生异常退出,也能保证局部对象被析构从而保证资源被释放。
这里我们只有几点要注意:
1)因为auto_ptr析构的时候肯定会删除它所拥有的那个对象,所以两个auto_ptr不能同时拥有一个对象。像这样:
int* p = new int(0); auto_ptr<int> ap1(p); auto_ptr<int> ap2(p);
因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p,两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr
2)考虑下面这样用法
int* pa = new int[10]; auto_ptr<int> ap(pa);
因为auto_ptr的析构函数中删除指针用的是delete,而不是delete[],所以我们不应该用auto_ptr来管理一个数组指针。
与引用计数型智能指针不同的,auto_ptr要求对其“裸”指针的完全占用性。也就是说一个“裸“指针不能同时被两个以上的auto_ptr所拥有。那么,在拷贝构造或赋值操作时,我们必须做特殊的处理来保证这个特性。auto_ptr的做法是”所有权转移“,即拷贝或赋值的源对象失去对”裸“指针的所有权,所以,与一般的拷贝构造函数,赋值函数不同,auto_ptr的拷贝构造函数,赋值函数的参数为引用而不是常引用。当然,一个auto_ptr也不能同时拥有两个以上的”裸“指针,所以拷贝或赋值的目标对象将现释放期原来所拥有的对象。
这里的注意点是:
1)因为一个auto_ptr被拷贝或赋值后,其已经失去对原对象的所有权,这个时候,对这个auto_ptr的解引用操作是不安全的。如下:
int* p = new int(0); auto_ptr<int> ap1(p); auto_ptr<int> ap2 = ap1; cout<<*ap1; //错误,此时ap1只剩下一个NULL指针在手了
这种情况较为隐藏的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造),这样,传入的实参auto_ptr就失去了其对原对象的所有权,而原对象会在函数退出时被局部auto_ptr删除。如下:
void f(auto_ptr<int> ap) {cout<<*ap} auto_ptr<int> ap1(new int(0)); f(ap1); cout<<*ap1; //错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了
2)因为auto_ptr不具有值语义,所以auto_ptr不能被用在STL标准容器中。所谓值语义,是指符合以下条件的类型(假设有类A):
A a1;
A a2(a1);
A a3;
a3=a1;
那么:a2 == a1, a3==a1
很明显,auto_ptr不符合上述条件,STL标准容器要用到大量的拷贝赋值操作,并且假设其操作的类型符合以上条件。
shared_ptr
auto_ptr由于它的破坏性复制语义,无法满足标准容器对元素的要求,因而不能放在标准容器中。boost库中提供了一种新型的智能指针shared_ptr,它解决了在多个指针间共享对象所有权的问题,同时也满足容器对元素的要求,因而可以安全地放入容器中。
shared_ptr的作用同指针,但会记录有多少个shared_ptr共同指向同一个对象。这便是所谓的引用计数。一旦最后一个这个的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象就会被自动删除。这在非环形数据结构中防止资源泄漏很有帮助。