1. auto_ptr的意义
auto_ptr是一种智能指针,当系统异常退出的时候避免资源泄漏(内存)。
其他的资源还对应其他的智能指针。
2 auto_ptr的使用
std::auto_ptr<int> test(new int(1));
test将是一个auto_ptr的对象,使用一个int指针进行初始化。
test可以象其他指针一样使用,如使用* 使用->但是++不可以使用,以后也许会扩展,其实难对++做越界管理,也许可以放弃一些速度。
当使用auto_ptr的时候,必须使用显式的类型转化来初始化,如auto_ptr<classA>a(new classA)
而不能使用auto_ptr<classA> a = new classA;
3 auto_ptr所有权的转移
auto_ptr对所有权有严格的约定,一个auto_ptr只能控制一个指针,不能控制多个,当auto_ptr拥有一个指针的时候就不能在拥有其他的指针了。同时,不同的auto_ptr不能拥有同一个指针。但是存在这样的情况,不同的auto_ptr可能控制着相同的对象,不要出现这样的情况。
auto_ptr<int> a(new int); 1
auto_ptr<int> b(a); 2
auto_ptr<int> c = b; 3
auto_ptr<int> d(int); 4
d = c; 5
当2发生的时候,a将所有权放弃,b获得所有权。
当3发生时, b将放弃所有权,c获得所有权
当5发生时, c的所有权给d,d将释放自己的所有权,并把对象删除掉。
4 来源和接收
当auto_ptr以参数的形式传递给函数的时候,作用域同样发生了变化,从之前的作用域变成了函数内部的作用域。过程时这样的,当有auto_ptr作为参数传递到函数内部的时候,首先会交接自己的所有权,所有权交给了函数的参数,作用域编程了函数的内部。函数退出时,函数内部的临时变量都会离开作用域,包括传递进来接收到的auto_ptr,auto_ptr会释放自己保存的资源。在函数退出后,申请的资源就消失了,这种情况可能很出乎人的预料,因为c++一般传递参数都是传值,如何都不会改变变量本身的作用域的。
另一个使用方式,在函数内部返回一个指针,可以避免内存泄漏
#include <iostream>
using namespace std;
template<class T>
auto_ptr<T> getAutoPtr()
{
auto_ptr<T> rt(new T);
return rt;
}
int main(void)
{
auto_ptr<int> rt =getAutoPtr<int>();
cout<< "["<<*rt<< "]"<< endl;
return 0;
}
函数的参数可以是auto_ptr<classT>& 类型,当传递auto_ptr的引用的时候,有的时候会传递所有权,有的时候不会,所以永远不要这样做。
当使用const auto_ptr<classT>& 的时候,这个时候如果转移所有权会出错,但是这里存在一个问题,const的意义在于不可以更改传递的参数的数据,但是在这里我们想实现的是不需要传递所有权。这个特性主要应用在模板上,当使用auto_ptr实例话对象的时候也不会出现问题。
所以const auto_ptr<classT>&的意思是不能转移所有权,而不是不能更改数据,这里和c++信息是不一样的,还是少使用好一些。
auto_ptr当作类的成员变量可以避免,当类在构造的时候出现异常,这样如果内存被分配了是不会被释放的,因为如果构造没有完成是不会调用析构函数的,这个时候就没有办法来释放申请的资源了。可以使用
auto_ptr来避免这样的事情发生,如果不想在类种转换所有权,可以使用const来修饰。
5 使用auto_ptr的误区
1 两auto_ptr不要引用同一个对象(t*),这样如果一个auto_ptr释放了资源,另一个或多个就无法获得引用对象的信息。
2 auto_ptr不可以保存array,因为在释放的时候是使用delete,这样就会引起未知的问题。
3 auto_ptr设计成在异常发生的时候,避免内存泄漏,不要乱用。
4 不要在容器中使用auto_ptr,因为无法满足容器的一些要求,比如在copyconstruct和 “=”操作时,会转移所有权。
---------------------------------------
auto_ptr 类
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 exceptionthis is not caught inside Func
delete pNum;
}
我们可以看到,假定在 new 和 delete 之间抛出了一个未捕获的异常,那么内存泄漏发生了,deletepNum 未被执行。这就是非异常安全的内存分配。同样还有一个类似的例子:
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(newint(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(newstd::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(newstd::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 saysthere is supposed to be an empty throw
// specification here, butomitting it is standard conforming. Its
// presence can be detected onlyif _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-daycompilers do not support the language
// features that these conversionsrely 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
{ returnauto_ptr_ref<_Tp1>(this->release()); }
template <class _Tp1>operator auto_ptr<_Tp1>() __STL_NOTHROW
{ returnauto_ptr<_Tp1>(this->release()); }
#endif /* auto ptr conversions && member templates */
};