void func1 ( )
{
int * p = new int(1024);
// ... an exception occurred
delete p;
}
上面是个很露骨的”异常不安全“的例子。
一旦发生异常,p申请的内存将得不到释放。
一个简单的办法是:
int * p = new int(1024);
try{
// ... an exception occurred
}catch(...)
{
delete p;
throw;
}
delete p;
还有种方法是定义一个简单的类Ptr,它含有一个指针成员。该类的析构函数会释放这个指针所指对象的内存。
void func1 ( )
{
Ptr<int> p ( new int(1024) );
// ... an exception occurred
// always ok
}
因为不论有没有异常发生,都要退func1的栈,p是在栈上的对象,退栈时自动调用了它的析构函数,从而释放了int的内存。
不论有没有异常发生,都要退栈;可以把这点看做类似C#的finally子句,来利用。
不过上面Ptr类只是一个示范,too naive,具体请参照C++ prmer的第13章 复制控制 管理指针成员,讲述了引用计数,和std、boost中的智能指针,会用就可以了。
不只是释放内存,对于其它的资源也是一样的,比如加锁与释放锁。
void func ( void * area)
{
lock(area);
// ... if an exception occurred...Oops
unlock(area);
}
应该改为:
void func ( void * area)
{
Locker lk ( area );
// ...
}// lk.Locker::~Locker();
在构造函数中分配资源,在析构函数中释放。即所谓的RAII:
Resource Acquisition Is Initialization
资源获取即初始化
奇怪的是C++ primer书上虽然讲了引用计数,却只介绍了不使用计数的std::auto_ptr?
std::auto_ptr的设计使它具有以下四条行为逻辑(四条限制):
1)std::auto_ptr只能保存指向动态分配对象的指针。因为std::auto_ptr本身被撤销的时候会动态释放它保存的指针所指向的内存,所以得是动态分配的。
2)不要让两个std::auto_ptr指向同一个对象,否则将来这两个std::auto_ptr被撤销的时候,那个对象被释放两次!
3)std::auto_ptr不能保存指向动态分配的数组的指针。因为std::auto_ptr中使用的是delete,而不是delete[]。
4)std::auto_ptr之间的复制和赋值被定义成了“转移式”,被转移的那个std::auto_ptr变成未绑定的。复制和赋值之后两个对象不相等,因此,不能将std::auto_ptr存储在标准库容器中。
但std::auto_ptr自C++11起,被标记为deprecated,不赞成使用的,而是用它的替代unique_ptr。
相关历史请看这儿:http://www.cppblog.com/eXile/archive/2009/11/16/101062.html
1994年. Greg Colvin向C++标准委员会提出了自己设计的智能指针:auto_ptr和counted_ptr。auto_ptr实现基本的RAII管理,不可复制;counted_ptr采用引用计数实现了一个可复制的智能指针。两者用于不同的场合。 但是标准委员会最终只通过了auto_ptr,并且对auto_ptr加入了一个古怪的“所有权转移”语义。后来auto_ptr和counted_ptr进入了Boost C++ 库,改名为scoped_ptr和shared_ptr。std的auto_ptr与unique_ptr之间的区别:http://blog.csdn.net/weiwenhp/article/details/8708281,简言之,后者可以支持数组、可以放入容器、不支持拷贝(而是使用move语义)
这里是std中的智能指针: http://en.cppreference.com/w/cpp/memory
also so see the http://msdn.microsoft.com/zh-cn/library/hh279674.aspx