嗯嗯,C语言和C++都有一个专为调试而准备的工具函数,就是 assert()函数。
这个函数是在C语言的 assert.h 库文件里定义的,所以包含到C++程序里我们用以下语句:#include
如果为真,Do nothing!
如果为假,Do something!
看下边演示:test.cpp(课件及源代码下载)
演示中,我们看到 assert()函数可以帮助我们调试程序。
我们可以利用它在某个程序里的关键假设不成立时立刻停止该程序的执行并报错,从而避免发生更严重的问题。
另外,除了结合 assert()函数,在程序的开发、测试阶段,我们还可以使用大量的 cout 语句来报告在程序里正在发生的事情。
一种与程序猿有关,在开发、测试和调试程序的过程中,程序猿需要尽可能详细的信息来查找和纠正各种潜在的运行时错误。
另一种情况与最终用户有关,在使用一个程序的过程中,错误处理应该把用户的感受摆在第一位!
在理想的情况下,程序发布之前,它里边的所有错误都应该被发现和改正过来。
只可惜这是几乎不可能的,就连微软这样的大公司也做不出这样的保证!
注意,我们这几节课讨论的内容是从程序猿的角度展开的。这里介绍的一些技巧不应该用在一个即将交付给最终用户的应用程序里。
因为这样的用户使用体验会差到极点!
最为一条原则:最终用户看到的错误信息应该既专业又清晰,不能轻易中断程序,不能充满技术细节!
同样为了对付潜在的编程错误(尤其是运行时的错误),捕获异常是一种完全不同的办法。
简单地说,异常(exception)就是与预期不相符合的反常现象。
1. 安排一些C++代码(try语句)去尝试某件事 —— 尤其是那些可能会失败的事(比如打开一个文件或申请一些内存)
2. 如果发生问题,就抛出一个异常(throm语句)
3. 再安排一些代码(catch语句)去捕获这个异常并进行相应的处理。
捕获异常的基本语法如下:
try
{
// Do something.
// Throw an exception on error.
}
catch
{
// Do whatever.
}
Pay attention!每条 try 语句至少要有一条配对的 catch 语句。
必须定义 catch 语句以便让它接收一个特定类型的参数。
C++还允许我们定义多条 catch 语句,让每条 catch 语句分别对应着一种可能的异常类型:
catch(int e){ … }
catch(bool e){ … }
catch(…){ … }
最后一条 catch 语句可以捕获任何类型的异常。
在程序里,我们可以用 throw 保留字来抛出一个异常:throw 1;
在某个 try 语句块里执行过 throw 语句,它后面的所有语句(截止到这个 try 语句块末尾)将永远不会被执行。
与使用一个条件语句或 return 语句相比,采用异常处理机制的好处是它可以把程序的正常功能和逻辑与出错处理部分清晰地划分开而不是让它们混杂在一起。
你可以在定义一个函数时明确地表明你想让它抛出一个异常,为了表明你想让它抛出哪种类型的异常,可以使用如下所示语法:
type functionName(arguments) throw(type);
如果没有使用这种语法来定义函数,就意味着函数可以抛出任意类型的异常。
注:有些编译器不支持这种语法,则可省略 throw(type) 部分。
如何使用异常是一个很容易引起争论的话题。
有些程序员使用异常来处理几乎所有的错误,但C++的创始人Bjarne Stroustrup(BS君)觉得它们正在被滥用。
所以使用异常的基本原则是:应该只用它们来处理确实可能不正常的情况。
作为一条原则,在构造器和析构器里不应该使用异常。
一位非常有经验的程序猿在这些方法里成功地使用了异常是有可能的,但稍有不慎就会导致严重的问题。
如果 try 语句块无法找到一个与之匹配的 catch 语句块,它抛出的异常将中止程序的执行。
在C++标准库里有个一名为 exception 的文件,该文件声明了一个 exception 的基类。
可以用这个基类来创建个人的子类以管理异常。
有经验的程序猿常常这么做,而如此抛出和捕获的是 exception 类或其子类的对象。
如果你打算使用对象作为异常,请记住这样一个原则:以”值传递”方式抛出对象,以”引用传递”方式捕获对象。