转自:http://blog.csdn.net/xkyx_cn/archive/2009/03/05/3960569.aspx
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。
最后,附上 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 */
};