C语言和C++都有一个专为调试而准备的工具函数,就是assert()函数。
这个函数实在C语言的assert.h库文件里定义的,所以包含到C++程序里我们用一下语句:
#include <cassert>
assert()函数需要有一个参数,它将测试这个输入参数的真or假状态。
如果为真,Do nothing!
如果为假,Do something!
例子:
#include <cassert>
int main()
{
int a = 20;
assert(a == 65);
return 0;
}
这就出问题了。
我们可以利用它在某个程序里的关键假设不成立时立刻停止该程序的执行并报错,从而避免发生更严重的问题。
另外,除了结合assert()函数,在程序的开发、测试阶段,我们还可以使用大量的cout语句来报告在程序里正在发生的事情。
捕获异常
同样为了对付潜在的编程错误(尤其是运行时的错误),捕获异常是一种完全不同的方法。
简单来说,异常(exception)就是与预期不相符合的反常现象。
基本使用思路:
1. 安排一些C++代码(try语句)去尝试某件事--尤其是那些可能会失败的事(比如打开一个文件或申请一些内存)
2. 如果发生问题,就抛出一个异常(throw语句)
3. 再安排一些代码(catch语句)去捕获这个异常并进行相应的处理。
捕获异常的基本语法如下:
try
{
//Do something
//Throw an exception on error.
}
catch
{
//Do whatever
}
注意:每条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)。
下面来一个例子:
#include <iostream>
#include <climits>
unsigned long returnFactorial(unsigned short num) throw(const char*); //就是上面说的那个语法,定义一个函数抛出什么类型的异常,这里抛出的是char*类型的异常
int main()
{
unsigned short num = 0;
std::cout << "请输入一个整数:";
while (!(std::cin >> num) || (num < 1))
{
std::cin.clear(); //清除状态
std::cin.ignore(100, '\n'); //清除缓冲区
std::cout << "请输入一个整数:";
}
std::cin.ignore(100, '\n');
try
{
unsigned long factorial = returnFactorial(num);
std::cout << num << "的阶乘值是:" << factorial;
}
catch (const char *e)
{
std::cout << e;
}
return 0;
}
unsigned long returnFactorial(unsigned short num) throw(const char *)
{
unsigned long sum = 1;
unsigned long max = ULONG_MAX;
for (int i = 1; i <= num; i++)
{
sum *= i;
max /= i;
}
if (max < 1)
{
throw "该基数太大,无法在该计算机中计算出阶乘值。\n";
}
else
{
return sum;
}
}
tips
使用异常的基本原则是:应该只用它们来处理确实可能不正常的情况。
作为一条原则,在构造器和析构器里不应该使用异常。一位非常有经验的程序员在这些方法里成功地使用了异常是可能的,但稍有不慎就会导致严重的问题。
如果try语句块无法找到一个与之匹配的catch语句块,它抛出的异常将终止程序的执行。
在C++标准库里有一个名为exception的文件,该文件声明了一个exception的基类。可是用这个基类来创建个人的子类以管理异常。
有经验的程序员常常这么做,而如此抛出和捕获的是exception类或其子类的对象。
如果你打算使用对象作为异常,请记住这样一条原则:以“值传递”方式抛出对象,以“引用传递”方式捕获对象。