auto_ptr 类是一个模板类,它被定义在 memory 头文件中。
auto_ptr 类可以用于管理由 new 分配的单个对象,但是无法管理动态分配的数组(我们通常不会使用数组,而是使用 vector 代替数组)。auto_ptr 在拷贝和赋值的时候有不寻常的行为,因此 auto_ptrs 不能被保存在 stl 的容器中。当 auto_ptr 离开了自己的作用域或者被销毁,由 auto_ptr 管理的对象也会被销毁。
1. 为异常安全的内存分配(Exception-Safe Memory Allocation)使用 auto_ptr
我们看下面的代码:
void Func()
{
int *pNum = new int(100);
// code that throws an exception this is not caught inside Func
delete pNum;
}
我们可以看到,假定在 new 和 delete 之间抛出了一个未捕获的异常,那么内存泄漏发生了,delete pNum 未被执行。这就是非异常安全的内存分配。同样还有一个类似的例子:
void Func()
{
CMember* pMember = new CMember;
CTeam* pTeam = QueryTeam(idTeam);
if (NULL == pTeam)
return; // Memory leaked
pTeam->Add(pMember);
}
我们可以看到在动态分配了一个 CMember 对象后,由于出现某种情况函数 return,而 CMember 对象却没有得到释放。
假如我们使用 auto_ptr 类代替,那么内存将会被自动释放:
void Func()
{
auto_ptr<int> ap(new int(100));
// …
}
2. 绑定 auto_ptr 到一个指针:
通常的情况,我们这样使用 auto_ptr 类:
auto_ptr<CClass> pObject(new CClass);
这样,我们就完成了 auto_ptr 到指针的绑定。注意,以下这种表达是错误的:
auto_ptr<CClass> pObject = new CClass;
当 auto_ptr 离开自己的作用域时,绑定在 auto_ptr 上的指针指向的对象将被释放。
3. 使用 auto_ptr
auto_ptr 类重载了解引用操作符(*)和箭头操作符(->),这样,我们能够想使用内置指针一样的使用 auto_ptr,例如:
class CClass
{
public:
void Print();
// …
}
int main()
{
std::auto_ptr<CClass> pObj(new CClass);
pObj->Print(); // the same (*pObj).Print();
}
4. auto_ptr 的赋值和拷贝操作符
内置的指针和 auto_ptr 的区别在于赋值和拷贝,这个需要特别注意。如果是内置指针的拷贝(赋值),那么结果是这两个指针指向同一个对象,而 auto_ptr 则不一样:
auto_ptr<string> ap1(new string("Stegosaurus");
auto_ptr<string> ap2(ap1);
执行完上面的语句后,ap1 将处于非绑定(unbound)状态,并不指向任何对象,被管理的指针的所有权从 ap1 转到 ap2,即指针将被 ap2 管理(包括内存的释放等),而不是 ap1。
赋值(拷贝)操作可能导致内存的释放,例如:
std::auto_ptr<std::string> ap1(new std::string("Pterodactry"));
std::auto_ptr<std::string> ap2;
ap1 = ap2; // 释放内存
正是因为拷贝和赋值会破坏右操作数,所以 auto_ptrs 无法存储在stl 的容器中。stl 的容器需要两个对象在拷贝或者赋值之后相等。
5. auto_ptr 类的函数
1)默认构造函数
auto_ptr<int> pNum; // 没有指向任何对象
*pNum = 100; // 错误
2)get 函数,返回指向对象的指针
if (NULL == pNum.get()) // 指针是否为空
注意,对于处于非绑定状态的 auto_ptr 调用 get 将返回 0。不要做这样的操作:
auto_ptr<int> pNum2(pNum1.get()); // 没有转交所有权
那么 pNum1 管理的指针将被析构两次,这必然带来严重的后果。
3)reset 函数,重设需要管理的指针,首先 auto_ptr 会删除当前管理的对象,然后再设置新的对象的指针。另外:
pNum = pNum; // 不会发送任何事情,安全
pNum.reset(pNum->get()); // 不会发送任何事情,安全
4)release 函数释放所有权,返回它管理的指针,不删除指针指向的对象:
auto_ptr<int> pNum2(pNum1.release()); // 等同于下句
auto_ptr<int> pNum2(pNum1); // 等同于上句
6. 编译器的问题
vc6 下的 auto_ptr 并不是标准的,例如:
std::auto_ptr<std::string> ap1(new std::string("Pterodactry"));
std::auto_ptr<std::string> ap2(ap1);
std::cout << *ap2 << std::endl; // 正常执行
7. 被管理的类的析构函数
如果被管理的类的析构函数为私有的,那么将无法使用 auto_ptr。
补充:
1.auto_ptr只能用于防止资源泄漏.也就是说是用auto_ptr的唯一目的在于使一个对象必然被delete.无论发生什么情况,这个对像都将在某个时候被释放,而不会泄漏.所以,下面直接将auto_ptr所控制的对象成为资源.
2.auto_ptr理论上没有引用计数的能力.所以所有依赖于引用计数的特性auto_ptr都不具备.例如:auto_ptr不能处理"野指针"的问题。
3.auto_ptr对象总是认为自己是所包含的资源的唯一拥有者.这导致:
3.1)一个auto_ptr对象被析构时,如果其持有一个有效的资源,那么这个资源必然被立即delete
3.2)一个auto_ptr对象被复制(赋值/拷贝构造/参数传递/函数返回)时:
3.2.1)目标auto_ptr中原有的资源(如果有),必然被立即delete.
3.2.2)目标auto_ptr获得源auto_ptr原来所持有的资源.
3.2.3)源auto_ptr失去其所持有的资源,成为一个空闲的对象.
4.auto_ptr作为函数的参数和返回值的时候,要特别注意,形参和实参的结合过程中存在auto_ptr的复制.从而导致资源控制权的转移.从下面的一段代码可以看出问题的严重性:
1: //这是一个危险的用法
2: template <class T>
3: void bad_print(std::auto_ptr<T> p) //形参P获得了资源的控制权
4: {
5: //检测p是否持有一个资源
6: if (p.get() == NULL) {
7: std::cout << "NULL";
8: }
9: else {
10: std::cout << *p;
11: }
12: } //函数退出的时候,p的作用域结束.资源也就被释放了.
5.关于const auto_ptr,const auto_ptr意味着这个auto_ptr对象 永远而且只能 持有这个资源.这意味着:
5.1)不能将一个const auto_ptr赋值给其它auto_ptr.(这一点和其他对象有很大的不同.而且直接导致auto_ptr不能被放到STL的容器中去).
5.2)可以使用const auto_ptr&作为参数类型来传递资源,而不会导致资源被意外delete.
5.3)被持有的资源本身可以被修改.
6.auto_ptr作为成员变量,也可以用这种方法来防止资源泄漏.但是需要注意对auto_ptr的控制权问题.这涉及到
6.1)拷贝构造函数和赋值操作符重载.是否真的转移控制权还是让两个auto_ptr都控制同一个对象? 多个auto_ptr控制同一个对象极容易导致野指针.
6.2)是否有函数返回了此auto_ptr.一般应该直接返回此资源,同时要保证此资源没有被其他auto_ptr控制,同时要保证在此auto_ptr所在的对象被销毁后,没有人会再使用此资源.
6.3)最好是作为const auto_ptr来使用.
7.总结:
1.尽可能只将auto_ptr对象作为一个函数的局部变量使用;
2.如果一个大函数重构导致某个auto_ptr需要在多个函数中使用,通过const auto_ptr&方式直接传递过去;
3.永远不要返回auto_ptr对象,如果一定要这么用,就返回 const auto_ptr&,如果const auto_ptr&不能满足要求,修改你的设计.
4.不要将auto_ptr放到任何容器中去.当然 我们可以用类似于vector<auto_ptr<Object>*>这么来用.但是很无聊.
5.将auto_ptr作为成员变量要慎重. 如果有此需要google以下其它带有引用计数的智能指针吧.
最后,附上 SGI 的 auto_ptr 源码:
template <class _Tp> class auto_ptr {
private:
_Tp* _M_ptr;
public:
typedef _Tp element_type;
explicit auto_ptr(_Tp* __p = 0) __STL_NOTHROW : _M_ptr(__p) {}
auto_ptr(auto_ptr& __a) __STL_NOTHROW : _M_ptr(__a.release()) {}
#ifdef __STL_MEMBER_TEMPLATES
template <class _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) __STL_NOTHROW
: _M_ptr(__a.release()) {}
#endif /* __STL_MEMBER_TEMPLATES */
auto_ptr& operator=(auto_ptr& __a) __STL_NOTHROW {
if (&__a != this) {
delete _M_ptr;
_M_ptr = __a.release();
}
return *this;
}
#ifdef __STL_MEMBER_TEMPLATES
template <class _Tp1>
auto_ptr& operator=(auto_ptr<_Tp1>& __a) __STL_NOTHROW {
if (__a.get() != this->get()) {
delete _M_ptr;
_M_ptr = __a.release();
}
return *this;
}
#endif /* __STL_MEMBER_TEMPLATES */
// Note: The C++ standard says there is supposed to be an empty throw
// specification here, but omitting it is standard conforming. Its
// presence can be detected only if _Tp::~_Tp() throws, but (17.4.3.6/2)
// this is prohibited.
~auto_ptr() { delete _M_ptr; }
_Tp& operator*() const __STL_NOTHROW {
return *_M_ptr;
}
_Tp* operator->() const __STL_NOTHROW {
return _M_ptr;
}
_Tp* get() const __STL_NOTHROW {
return _M_ptr;
}
_Tp* release() __STL_NOTHROW {
_Tp* __tmp = _M_ptr;
_M_ptr = 0;
return __tmp;
}
void reset(_Tp* __p = 0) __STL_NOTHROW {
if (__p != _M_ptr) {
delete _M_ptr;
_M_ptr = __p;
}
}
// According to the C++ standard, these conversions are required. Most
// present-day compilers, however, do not enforce that requirement---and,
// in fact, most present-day compilers do not support the language
// features that these conversions rely on.
#if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && /
defined(__STL_MEMBER_TEMPLATES)
public:
auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW
: _M_ptr(__ref._M_ptr) {}
auto_ptr& operator=(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW {
if (__ref._M_ptr != this->get()) {
delete _M_ptr;
_M_ptr = __ref._M_ptr;
}
return *this;
}
template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW
{ return auto_ptr_ref<_Tp1>(this->release()); }
template <class _Tp1> operator auto_ptr<_Tp1>() __STL_NOTHROW
{ return auto_ptr<_Tp1>(this->release()); }
#endif /* auto ptr conversions && member templates */
};
转载自:http://blog.csdn.net/xkyx_cn/archive/2009/03/05/3960569.aspx
http://www.cnblogs.com/zhuliangxiong/archive/2009/10/10/1580419.html