C++ 异常处理(一)

时间:2022-12-12 19:53:50

例:

计算两个数的调和平均数(两字数字的倒数的平均数的倒数)

表达式为:2.0* x * y / (x + y)

这里x = -y是不允许的,因为上述公式将导致被0除。

方法一:调用 abort() 函数

调用 abort() 函数,需头文件:cstdlib (或 stdlib.h)

其典型实现是向标准错误流(即cerr使用的错误流)发送消息 abnormal program termination (程序异常终止),

然后终止程序。

它还返回一个随实现而异的值,告诉操作系统(如果程序是由另一个程序调用的,则告诉父进程),处理失败。

abort() 是否刷新缓冲区(用于存储读写到文件中的数据的内存区域)取决于实现。

如果愿意,也可以使用 exit() 函数,该函数刷新缓冲区,但不显示消息。

#include <iostream>
#include <cstdlib>

double hmean(double a, double b);

int main()
{
double x, y, z;
std::cout << "Enter two number: ";
while (std::cin >> x >> y)
{
z = hmean(x, y);
std::cout << "x = " << x << " , "
<< "y = " << y << " , "
<< "z = " << z
<< "\n";
std::cout << "Enter next set of numbers <q to quit>: ";
}

std::cout << "main() function end\n";
return 0;
}

double hmean(double a, double b)
{
if ( a == -b)
{
std::cout << "error in function : hmean()\n";
std::abort();//程序在这里中断
}
return 2.0 * a * b / ( a + b);
}
注意:hmean()函数中的abort()将直接终止程序,而不是先返回到main().


方法二:返回错误码

该例可以使用指针或引用参数来将值返回给调用程序,并使用函数的返回值来指出成功与否。

#include <iostream>
#include <cfloat> // (or float.h) for DBL_MAX

bool hmean(double a, double b, double * ans);

int main()
{
double x, y, z;
std::cout << "Enter two number: ";
while (std::cin >> x >> y)
{
if (hmean(x, y, &z))
{
std::cout << "x = " << x << " , "
<< "y = " << y << " , "
<< "z = " << z
<< "\n";
}
else
{
std::cout << "One value should not be the negative of the other - try again.\n";
}
std::cout << "Enter next set of numbers <q to quit>: ";
}

std::cout << "main() function end\n";
return 0;
}

bool hmean(double a, double b, double * ans)
{
if ( a == -b)
{
*ans = DBL_MAX;
return false;
}
else
{
*ans = 2.0 * a * b / ( a + b);
return true;
}
}
这里在hmean()函数中,用函数返回值确定是否成功,用指针参数返回给调用程序。


第三种:异常机制

try,catch,throw

#include <iostream>

double hmean(double a, double b);

int main()
{
double x, y, z;
std::cout << "Enter two number: ";
while (std::cin >> x >> y)
{
try
{
z = hmean(x, y); //异常可能的区域
}
catch (const char* s) //这里如果hmean()函数未发生异常,则catch(){}不执行
{
std::cout << s << std::endl;
std::cout << "Enter a new pair of numbers: ";
continue;
}
std::cout << "x = " << x << " , "
<< "y = " << y << " , "
<< "z = " << z
<< "\n";
std::cout << "Enter next set of numbers <q to quit>: ";
}

std::cout << "main() function end, Bye!\n";
return 0;
}

double hmean(double a, double b)
{
if ( a == -b)
throw "bad hmean() arguments: a = -b not allowed!"; //如运行到throw,则直接跳到catch,并传递throw的值
return 2.0 * a * b / (a + b);
}

流程:

执行完try块的中语句,


如果没发生异常,则程序跳过try块后面的catch块,直接执行程序后面的第一条语句。


如果发生异常,则终止hmean()函数的执行,程序往回搜寻发现函数hmean()是从main()中的try块中调用的,因此查找与try{}匹配的catch(){}块,

程序中唯一的一个catch块的参数为char*,因此与它匹配。

程序将throwb出的字符串"bad hmean() arguments: a = -b not allowed!"赋值给变量s, 然后执行程序中的代码。

注意:如果函数发生了异常,而没有try块或没有与之匹配的 catch 块,将会发生什么呢?

            在默认情况下:程序将最终调用abort()函数。但我们也可以修改这种行为。


第四:将对象作为异常类型

一般,引发异常的函数将传递一个对象。

优点之一:可以使用不同的异常类型来区分不同的函数在不同的情况下发生的异常。

另外:对象可以携带信息,程序员可以根据这些信息来确定引发异常的原因。

同时:catch 块可以根据这些信息来决定采取什么样的措施。

例:

这里先定义一个异常类,以便生成对象。

//exc_mean.h
#include <iostream>

class bad_hmean
{
private:
double v1;
double v2;
public:
bad_hmean(double a = 0, double b = 0) : v1(a), v2(b){}
void mesg();
};

inline void bad_hmean::mesg()
{
std::cout << "bad_hmean: " << v1 << " , " << v2 << " : "
<< " invalid arguments: a = -b\n";
}

class bad_gmean
{
public:
double v1;
double v2;
bad_gmean(double a = 0, double b = 0) : v1(a), v2(b){}
const char * mesg();
};

inline const char * bad_gmean::mesg()
{
return "gmean() arguments should be >= 0\n";
}

//error.cpp

#include <iostream>
#include <cmath>
#include "exc_mean.h"

double hmean(double a, double b);
double gmean(double a, double b);

int main()
{
using std::cout;
using std::cin;
using std::endl;

double x, y, z;

cout << "Enter two numbers: ";
while (cin >> x >> y)
{
try{
z = hmean(x, y);
cout << x << " " << y << " " << z << endl;
cout << gmean(x, y) << endl;
cout << "Enter next set of numbers <q to quit>: \n";
}
catch (bad_hmean& bg)
{
bg.mesg();
cout << "Try again.\n";
continue; //继续循环
}
catch (bad_gmean& hg)
{
cout << hg.mesg();
cout << hg.v1 << " " << hg.v2 << endl;
cout << "Sorry, you don't get to play any more.\n";
break; //跳出循环
}
}
cout << "Bye!\n";
return 0;
}

double hmean(double a, double b)
{
if (a == -b)
throw bad_hmean(a, b); //抛出bad_hmean对象
return 2.0 * a * b / (a + b); //a和b的调和平均数
}
double gmean(double a, double b)
{
if (a < 0 || b < 0)
throw bad_gmean(a, b); //抛出bad_gmean对象
return std::sqrt(a * b); //a*b的平方根
}
输出:

Enter two numbers: 4 12
4 12 6
6.9282
Enter next set of numbers <q to quit>:
5 -5
bad_hmean: 5 , -5 : invalid arguments: a = -b
Try again.
5 -2
5 -2 -6.66667
gmean() arguments should be >= 0
5 -2
Sorry, you don't get to play any more.
Bye!

第五:

exc_mean.h
//代码同上


//error2.cpp

#include <iostream>
#include <cmath>
#include <string>
#include "exc_mean.h"

class demo
{
private:
std::string word;
public:
demo(const std::string& str)
{
word = str;
std::cout << "demo: " << word << " created\n";
}
~demo()
{
std::cout << "demo: " << word << " destroyed\n";
}
void show() const
{
std::cout << "demo: " << word << " showed!\n";
}
};

double hmean(double a, double b);
double gmean(double a, double b);
double means(double a, double b);

int main()
{
using std::cout;
using std::cin;
using std::endl;

double x, y, z;
{
demo d1("found in block in main()");
cout << "Enter two numbers: ";
while (cin >> x >> y)
{
try{
z = means(x, y);
cout << x << " " << y << " " << z << endl;
cout << "Enter next set of numbers <q to quit>: \n";
}
catch (bad_hmean& bg)
{
bg.mesg();
cout << "Try again.\n";
continue; //继续循环
}
catch (bad_gmean& hg)
{
cout << hg.mesg();
cout << hg.v1 << " " << hg.v2 << endl;
cout << "Sorry, you don't get to play any more.\n";
break; //跳出循环
}
}
}
cout << "Bye!\n";
cin.get();
cin.get();
return 0;
}

double hmean(double a, double b)
{
if (a == -b)
throw bad_hmean(a, b); //抛出bad_hmean对象
return 2.0 * a * b / (a + b); //a和b的调和平均数
}
double gmean(double a, double b)
{
if (a < 0 || b < 0)
throw bad_gmean(a, b); //抛出bad_gmean对象
return std::sqrt(a * b); //a*b的平方根
}
double means(double a, double b)
{
double am, hm, gm;
demo d2("found in means()");
am = (a + b) / 2.0;
try{
hm = hmean(a, b); //如果异常,将先触发means中的catch块,再触发main中的catch块
gm = gmean(a, b); //如果异常,means中将无法处理,这时异常被传递给main()中的相应的catch块
}
catch(bad_hmean& bg) //这里的catch块触发后,将会再触发main()中的
{
bg.mesg();
std::cout << "caught in means()\n";
throw; //rethrow the exception
//当这里发生异常时,仍然为d2调用了析构函数
//原因估计是thow完,运行至catch块},后直接至means函数块的},这时触发了d2的析构
}
d2.show();
return (am + hm + gm) / 3.0;
}

输出:

demo: found in block in main() created
Enter two numbers: 6 12
demo: found in means() created
demo: found in means() showed!
demo: found in means() destroyed
6 12 8.49509
Enter next set of numbers <q to quit>:
6 -6
demo: found in means() created
bad_hmean: 6 , -6 : invalid arguments: a = -b
caught in means()
demo: found in means() destroyed
bad_hmean: 6 , -6 : invalid arguments: a = -b
Try again.
6 -8
demo: found in means() created
demo: found in means() destroyed
gmean() arguments should be >= 0
6 -8
Sorry, you don't get to play any more.
demo: found in block in main() destroyed
Bye!