C++ 的异常处理
头一次发文章,也不知道是不是放在这里...
我们的程序总是会碰到一些不可预料的情况:当读取一个文件失败、当初始化驱动出错、当内存不足…。这些都给我们的程序的正常运行带来了麻烦。在 C 中,大家习惯于用函数的返回值来标志这些错误当然你在 C++ 中仍然可以也这么干;在 C++ 中,引入了一种错误处理机制 异常处理 Exception Handling。
一般的异常处理的代码写法是这样的:
int
main( )
{
try
{
//
你的程序代码
}
catch
( type1
&
ref1 )
{
//
针对 throw type1 对象的处理代码
}
catch
( type2
&
ref2 )
{
//
针对 throw type2 对象的处理代码
}
catch
(
)
{
//
剩下的所有错误
}
}
可以看到程序被 try , catch关键字分成了几个部分。try 块中放的是程序正常运行的代码;catch块里则是相应的错误处理代码。对应一个 try 可以有几个 catch , 不同的 catch 通过它们的参数来捕获不同的异常。
当在try中的代码运行中出错,我们可以通过 throw 关键字来抛出异常:
if( size < 0 )throw MyException(" logic error ");
这里抛出了一个自定义的异常类型,你可以抛出任何你想要的异常类型,然后用对应的 catch 将其捕获( 实际上 C++ 已经中内建了一些异常类型,感兴趣的话可以去查阅《Thinking in C++ VOL 2》等书籍 )。对应上面的 throw 我们的 catch 需要这样写:
catch( MyException & my_exception )
{
// 处理错误
}
如果这里的 catch 捕获了这个异常后发现搞不定它,那么它得负责任得将异常向上递交:
catch
( MyException
&
my_exception )
{
//
我搞不定
throw
;
}
这样程序会找更外面的 try 对应的 catch 来处理这个异常。
如前面所说,当我们的程序执行到 throw 的时候,它会寻找对应的 catch 转而执行 catch
中的异常处理代码,等等~在跳转之前,程序会自动析构所有栈上的对象,就是也就是静态构造的对象,但是对于动态创建的对象会有些麻烦,即使你在某处
delete 掉了它,如果在此之前有异常抛出,很有可能这个 delete 根本不会被执行…
void
RunComputer( )
{
Computer
*
p_pc
=
new
Computer( );
p_pc.Run( );
//
如果这里抛出异常?
delete p_pc;
}
这种情况将导致内存泄漏,delete p_pc 有可能被跳过。为了避免这种情况我们可以在异常抛出的地方将对应的资源先释放一次:
void RunComputer( )
{
Computer * p_pc = new Computer( );
try
{
p_pc.Run( ); // 抛出异常
}
catch(
)
{
delete p_pc; // delete 了以后向上递交
throw;
}
delete p_pc;
}
这样看上去是不是很不爽?为一个 new 要写两个 delete 。
还有个更好的方法来解决这种问题,那就是使用 std::auto_ptr ,它的原理是将你的指针存放在一个静态对象中,当 throw 发生时,auto_ptr 会被析构,你的指针也会自动的被 delete。
void RunComputer( )
{
std::auto_ptr<Computer> p_pc( new Computer( ) );
p_pc.Run( ); // 抛出异常也不怕
// 无论是 throw 跳出 还是 函数返回 ,p_pc 都会自动被 delete
}
看上去这样算是圆满了。
那我们是不是该把指针都替换成 auto_ptr 呢 ? 且慢 , auto_ptr 是为了存放指针而设计的 , 为了保证它保存的指针不被删除两次 , 当我们拷贝一个 auto_ptr 时 , 新的 auto_ptr 会得到那个指针的所有权 .
未完...