auto_ptr
auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象。
1) 因为auto_ptr析构的时候肯定会删除他所拥有的那个对象,所以我们就要注意了,一个萝卜一个坑,两个auto_ptr不能同时拥有同一个对象。像这样:
1
2
3
|
int
*p=
new
int
(0);
auto_ptr<
int
>ap1(p);
auto_ptr<
int
>ap2(p);
|
因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。
2) 智能指针不能指向数组指针:
1
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的拷贝构造函数,赋值函数的参数为引用而不是常引用(const reference).当然,一个auto_ptr也不能同时拥有两个以上的“裸”指针,所以,拷贝或赋值的目标对象将先释放其原来所拥有的对象。
这里的注意点是:
1) 因为一个auto_ptr被拷贝或被赋值后, 其已经失去对原对象的所有权,这个时候,对这个auto_ptr的提领(dereference)操作是不安全的。如下:
1
2
3
4
|
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删除。如下:
1
2
3
4
5
6
7
8
|
void
f(auto_ptr<
int
>ap)
{
cout<<*ap;
}
auto_ptr<
int
>ap1(
new
int
(0));
f(ap1);
cout<<*ap1;
//错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了。
|
因为这种情况太隐蔽,太容易出错了, 所以auto_ptr作为函数参数按值传递是一定要避免的。或许大家会想到用auto_ptr的
指针或引用作为函数参数或许可以,但是仔细想想,我们并不知道在函数中对传入的auto_ptr做了什么, 如果当中某些操作使其失去了对对象的所有权, 那么这还是可能会导致致命的执行期错误。 也许,用const reference的形式来传递auto_ptr会是一个不错的选择。
2)我们可以看到
拷贝构造函数与赋值函数都提供了一个成员模板在不覆盖“正统”版本的情况下实现auto_ptr的隐式转换。如我们有以下两个类
1
2
|
class
base{};
class
derived:
public
base{};
|
那么下列代码就可以通过,实现从auto_ptr<derived>到auto_ptr<base>的隐式转换,因为derived*可以转换成base*类型
1
|
auto_ptr<base>apbase=auto_ptr<derived>(
new
derived);
|
3) 因为auto_ptr不具有值语义(value semantic), 所以auto_ptr不能被用在stl标准容器中。
所谓值语义,是指符合以下条件的类型(假设有类A):
1
2
3
4
5
6
|
A a1;
A a2(a1);
A a3;
a3=a1;
//那么
a2==a1,a3==a1
|
很明显,auto_ptr不符合上述条件,而我们知道stl标准容器要用到大量的拷贝赋值操作,并且假设其操作的类型必须符合以上条件。
4 辅助函数
1) get用来显式的返回auto_ptr所拥有的
对象指针。我们可以发现,标准库提供的auto_ptr既不提供从“裸”
指针到auto_ptr的隐式转换(
构造函数为explicit),也不提供从auto_ptr到“裸”指针的隐式转换,从使用上来讲可能不那么的灵活, 考虑到其所带来的安全性还是值得的。
2) release,用来转移所有权
3) reset,用来接收所有权,如果接收所有权的auto_ptr如果已经拥有某对象,必须先释放该对象。