提炼异常
try块介绍
- try块以关键字try作为开始,然后{},catch子句放在{}之后。据给自:
bool has_elem(Triangular_iterator first,Triangular_iterator last,int elem)
{
bool status=true;
try
{
while(first!=last)
{
if(*first==elem)
{
return status;
}
++first;
}
}
catch(iterator_overflow &iof)
{
log_message(iof.what_happened());
iog_message("check if iterators address same container");
//翻译:检查迭代器是否针对相同的容器
}
//try块内的程序代码执行时,如果有任何异常抛出,只捕获其中类型为
//iterator_overflow的异常。
status = false;
return status;
}
catch子句放在try块的末尾(“}”后),表示如果try块内有任何异常发生,便由接下来的catch子句加以处理。
try块对异常处理机制的作用(有了try块异常处理机制该怎么办)
上面例子中的*first表达式,调用重载*
运算符,重载*
运算符函数定义如下:
inline int Triangular_iterator::operator*()
{
check_integrity();
return Triangular::_elems[_index];
}
inline void Triangular_iterator::check_integrity()
{
if(_index>=Triangular::_max_elems)
{
throw iterator_overflow(_index,Triangular::_max_elems);
}
//...
}
- 如果_index>=Triangular::_max_elems,异常被抛出。
- 接下来异常处理机制开始查看异常在哪里被抛出,并判断是否位于try块内
- 如果异常在try块内被抛出(throw表达式在try块内),检验try块的"}"后的catch子句是否有处理该异常的能力,如果有能力处理该异常,则处理掉该异常,程序继续执行(check_integrity()(抛出异常的函数)的内容继续给他执行完,然后执行下面其他的内容)。
- 不过上面那个例子的throw表达式不在try块内,所以无法启动异常处理机制!
- 无法启动异常处理机制!check_integrity()(抛出异常的函数)的剩余内容不会被执行,因为异常处理机制终结了check_integrity()(抛出异常的函数)的执行权(因为抛出异常在check_integrity()函数里,异常没有处理,所以check_integrity()(抛出异常的函数)函数不可以再执行下去)
- 不过异常处理机制会在check_integrity()(抛出异常的函数)的调用处找可以处理异常的catch子句,这里check_integrity()函数的调用处是重载*运算符函数。
- 异常处理机制会判断发生异常的函数的调用操作是否在try块里。因为这个函数内部并没有处理异常,然而这个例子中check_integrity()函数并未在try块里。重载*运算符函数被异常处理机制终结执行权。
- 异常处理机制在重载运算符函数的调用处——即first这里,判断发生异常的函数的调用操作是否在try块里(层层递退,这样解释能明白吧?),
- 是的,发生异常的函数的调用操作是在try块里
try
{
while(first!=last)
{
if(*first==elem)
{
return status;
}
++first;
}
}
然后执行try{}之后的catch子句,即为
catch(iterator_overflow &iof)
{
log_message(iof.what_happened());
iog_message("check if iterators address same container");
//翻译:检查迭代器是否针对相同的容器
}
然后完成异常处理,继续执行
catch子句后的status = false;return status;
-
如果函数调用链不断被解开(层层递退),回到了main()函数里也找不到适配的catch子句该怎么办?
- C++规定,每个异常都应该被处理,因此,如果在main()函数里也找不到适配的catch子句或其他处理异常的代码,便调用标准库提供的terminate()函数——该函数默认的作用是中断整个程序执行
-
如果某一语句发生异常,而且这条语句不在try块内,那么该异常保证不会在某函数内被捕获处理。比如:
- 上个例子中的重载*运算符函数并没有将check_integrity()函数调用操作放在try块里,而check_integrity()函数有可能发生异常,为什么要这样做呢?
- 因为重载*运算符函数并未准备好处理其调用的check_integrity()函数里可能发生的异常,就算在check_integrity()函数里发生了异常,重载
*
运算符函数被中断了,以维持程序安全运行。
- 因为重载*运算符函数并未准备好处理其调用的check_integrity()函数里可能发生的异常,就算在check_integrity()函数里发生了异常,重载
- 不过怎么知道某个函数是否能够安全地忽略可能发生的异常呢?
- 如果某函数以抛出异常的方式来表现错误,普通的防范错误的操作如return 0,return false等就不必写上。无任何异常被抛出,可能发生异常的函数正常执行,否则该函数被中断。
- 上个例子中的重载*运算符函数并没有将check_integrity()函数调用操作放在try块里,而check_integrity()函数有可能发生异常,为什么要这样做呢?
-
把异常的产生限制在某函数内部,是因为清楚该函数对异常的产生对函数执行过程的重要性。某个异常可能产生的地方也许对项目而言很重要,所以要记录在这个地方上,以寻求合适的catch子句捕获异常并处理异常。不过,有的情况下可能产生异常的函数可以没有处理异常的能力,如果这个没有处理异常的能力对于可能产生异常的函数来说显得不重要,那么可以把调用可能产生异常的函数的地方和异常类隔离。
-
所以当函数的try块发生某个异常,但没有相应的catch子句将它捕获,此函数便会被中断,由异常处理机制接管,沿着函数调用链一路回溯,搜寻符合条件的catch子句。比如上面例子的has_elem()函数中,该函数的try块里可能会发生异常(在*first的重载
*
运算符函数里调用的check_integrity()函数里,(沿着函数调用链一路回溯体现在从右到左读一下这个括号里的句子)),但没有紧接着try块下的catch子句将可能发生的异常处理掉,于是异常被扔进异常类(上面例子是iterator_overflow异常类)里处理了。
初学C++在异常处理上犯的错误
- C++异常和segmentation fault(分割错误)或bus error(总线错误)这类硬件异常混淆在一起。
- 但是,面对任何一个被抛出的C++异常,都可以在程序某处找到一个相应的throw表达式(有的深藏在标准库里)