lofter地址:http://willkall.lofter.com/
C++11从boost库中引入了unique_ptr, shared_ptr, weak_ptr,并舍弃了c98的auto_ptr。
一、c++98 auto_ptr(参见《C++标准程序库》——孟岩/侯捷译)
C++标准程序库描述:“auto_ptr是一种智能指针,帮助程序员防止'被异常抛出时发生资源泄露'”。它在对象析构的时候自动释放资源,并模仿了原始指针的操作,重载了operator*和operator->,允许程序员向使用原始指针一样使用auto_ptr(但是没有重载所有的指针算术运算),并减少程序员显示的处理异常发生时代码的复杂度与失误。
注意:auto_ptr是严格的拥有权类智能指针,使用时注意以下几点
- auto_ptr之间不能共享拥有权
- auto_ptr对象通过赋值或构造转移拥有权,一旦拥有权转移,此auto_ptr所拥有的将是一个原始指针
- auto_ptr不适用于array
- auto_ptr不满足STL对容器元素的要求,因此不适用于STL容器。因为在拷贝和赋值之后,新的auto_ptr和旧的auto_ptr对象并不相等。
- 如果要阻止拥有权的转移,则应该在停止转移之前,将auto_ptr声明为const
- 不要使用auto_ptr的引用作为实参:因为你不知道拥有权到底有没有转移。如果你不需要转移拥有权,请使用const auto_ptr<class> &
1.auto_ptr的初始化
auto_ptr不允许使用一般的指针赋值来初始化,必须使用其构造函数来初始化。因为“根据一般指针生成一个auto_ptr”的那个构造函数被声明为explicit(禁止单参构造函数用于自动型别转换)
std::auto_ptr<ClassA> ptr1(new ClassA);//OK
std::auto_ptr<ClassA> ptr2 = new ClassA;//ERROR
2.auto_ptr拥有权转移
程序员必须防范一个原始指针被两个auto_ptr所拥有,且在auto_ptr丧失了拥有权后不可以再次使用此auto_ptr
//initialize an auto_ptr with a new obj
std::auto_ptr<ClassA> ptr1(new ClassA);
//copy the auto_ptr-->transfers the ownership from ptr1 to ptr2
std::auto_ptr<ClassA> ptr2(ptr1); //ptr1拥有权转移,以后不可再对ptr1使用operator*以及operator->
//like copy
std::auto_ptr<ClassA> ptr3 = ptr2; //ptr2拥有权转移给ptr3,以后不可再对ptr2使用operator*以及operator->
std::auto_ptr<ClassA> ptr4(new ClassA); //create a new auto_ptr::ptr4
ptr3 = ptr4; //here ptr3 delete the obj it own ,and then ptr4 transfers the ownership to ptr3。ptr4 own a nullptr
3.auto_ptr之起点和终点
auto_ptr拥有权的转移特性使得auto_ptr可以方便的实现数据的产生与销毁:在某函数中创建auto_ptr并以值形式返回,或者接受一个以值传递的auto_ptr对象,但不返回auto_ptr。
void sink(auto_ptr<ClassA>);//end the ownership transfer,delete source
auto_ptr<ClassA> generate(void);//generating an auto_ptr
generate()每次调用都new一个 classA对象,并将对象以及拥有权一起转移个调用端
sink(auto_ptr<classA>)每次调用有传入一个auto_ptr对象,但是不再返回auto_ptr。因此传入对象的拥有权在sink函数不再转移,并且auto_ptr拥有的资源也得到释放。
4.使用const auto_ptr
const auto_ptr意味着你不可以更改这个auto_ptr的拥有权——即你不能转移它也不能对其重新赋值。const auto_ptr比较类似于常指针(T *cosnt p)。
const auto_ptr<int> ptr(new int);
*ptr = 42;//OK
auto_ptr<int> ptr1(new int);
ptr1 = 58;
ptr = ptr1;//wrong,could not change ptr ownership,COMPLIE-TIME ERROR
5.auto_ptr源码注解(此处仅列出部分源码,完整源码见auto_ptr.h)
template<typename _Tp>
class auto_ptr
{
private:
_Tp* _M_ptr;
public:
typedef _Tp element_type;
explicit auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { } //禁止原始指针直接赋值时的隐式类型转换
auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { } //释放拥有权并置其拥有的原始指针为NULL
template<typename _Tp1>
auto_ptr(auto_ptr<_Tp1>& __a) throw() : _M_ptr(__a.release()) { } //2016-03-05 ????
auto_ptr&operator=(auto_ptr& __a) throw() //释放并转移拥有权
{
reset(__a.release());
return *this;
}
template<typename _Tp1> //2016-03-05 为啥要声明一个_TP1类型????
auto_ptr&operator=(auto_ptr<_Tp1>& __a) throw()
{
reset(__a.release());
return *this;
}
~auto_ptr() { delete _M_ptr; } //析构时释放资源——不适用于数组!!
element_type& operator*() const throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return *_M_ptr;
}
element_type* operator->() const throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return _M_ptr;
}
element_type* get() const throw() { return _M_ptr; } //获取原始指针——获取后切勿delete!!!!
element_type* release() throw() //显示的释放拥有权并置拥有的原始指针为NULL
{
element_type* __tmp = _M_ptr;
_M_ptr = 0;
return __tmp;
}
void reset(element_type* __p = 0) throw() //释放拥有权,并获取新的拥有权——可以自赋值
{
if (__p != _M_ptr)
{
delete _M_ptr;
_M_ptr = __p;
}
}
};
2016-03-06追加(纯属娱乐):
auto_ptr初始化的时候并不一定需要使用new class来初始化,直接使用一个原始指针即可。但是这样操作很可能出现问题——若用来初始化的原始指针不是一个new出来的存在于堆内存中的对象,则在后续的使用中会造成非法delete(对非new分配的指针调用delete)。以下为一个代码示例:
#include <iostream>
#include <memory>
using namespace std;
void stack_test(int *ip){
auto_ptr<int> aip(ip);
}
int main()
{
int *ip = new int(15);
stack_test(ip);
int c = 49;
int *ip1 = &c;
stack_test(ip1);
cout << "*api = " << *ip << endl;
cout << "*api2 = " << *ip1 << endl;
return 0;
}
以下为运行结果:
程序可以正常运行(运气不错),但*api得的值已经被覆盖,该指针已经无效。
说明:
1.不管是堆上的还是栈上的指针,都可以用来初始化auto_ptr
2.使用方法1可以使一个原始指针被多个auto_ptr所拥有(这样做非常危险)
3.栈上的内容并没有被修改——是否是因为没有调用delete或者是其他原因?因为c是栈上的元素,无法调用delete删除,在其生命周期中也不会被非法覆盖,所以*api2打印结果正常。但不知道auto_ptr对传入的栈上的指针是如何处理的?
4.另外此处还可说明:auto_ptr析构只是delete并置其拥有的原始指针为nullptr,但是用来初始化它的原始指针值(也就是其指向的地址)是不变的。
5.对上述代码尝试进行try...catch操作,但是没有捕获到异常(是否感到很不正常?)