❥关于C++之异常
Posted itzyjr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❥关于C++之异常相关的知识,希望对你有一定的参考价值。
throw异常try-catch捕获:
// error3.cpp -- using an exception
#include <iostream>
double hmean(double a, double b);
int main()
double x, y, z;
std::cout << "Enter two numbers: ";
while (std::cin >> x >> y)
try
z = hmean(x,y);
catch (const char * s)
std::cout << s << std::endl;
std::cout << "Enter a new pair of numbers: ";
continue;
// end of handler
std::cout << "Harmonic mean of " << x << " and " << y
<< " is " << z << std::endl;
std::cout << "Enter next set of numbers <q to quit>: ";
std::cout << "Bye!\\n";
return 0;
double hmean(double a, double b)
if (a == -b)
throw "bad hmean() arguments: a = -b not allowed";
return 2.0 * a * b / (a + b);
将对象用作异常类型:
// exc_mean.h -- exception classes for hmean(), gmean()
#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 << "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";
//error4.cpp – using exception classes
#include <iostream>
#include <cmath>
#include "exc_mean.h"
// function prototypes
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 << "Harmonic mean of " << x << " and " << y
<< " is " << z << endl;
cout << "Geometric mean of " << x << " and " << y
<< " is " << gmean(x, y) << endl;
cout << "Enter next set of numbers <q to quit>: ";
catch (bad_hmean& bg)
bg.mesg();
cout << "Try again.\\n";
continue;
catch (bad_gmean& hg)
cout << hg.mesg();
cout << "Values used: " << 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);
return 2.0 * a * b / (a + b);
double gmean(double a, double b)
if (a < 0 || b < 0)
throw bad_gmean(a, b);
return std::sqrt(a * b);
C++98中的异常规范(C++11中已将其摒弃):
double harm(double a) throw(bad_thind);// may throw bad_thing exception
double marm(double) throw();// doesn't throw an exception
异常规范的作用之一是,告诉用户可能需要使用try块。然而,这项工作也可使用注释轻松地完成。异常规范的另一个作用是,让编译器添加执行运行阶段检查的代码,检查是否违反了异常规范。这很难检查。例如,marm( )可能不会引发异常,但它可能调用一个函数,而这个函数调用的另一个函数引发了异常。另外,您给函数编写代码时它不会引发异常,但库更新后它却会引发异常。总之,编程社区(尤其是尽力编写安全代码的开发人员)达成的一致意见是,最好不要使用这项功能。而C++11也建议您忽略异常规范。
C++11确实支持一种特殊的异常规范:您可使用新增的关键字noexcept指出函数不会引发异常:
double marm() noexcept;// marm() doesn't throw an exception
知道函数不会引发异常有助于编译器优化代码。通过使用这个关键字,编写函数的程序员相当于做出了承诺。
引发异常时编译器总是创建一个临时拷贝,即使异常规范和catch块中指定的是引用。
class problem ... ;
...
void super() throw (problem)
...
if (oh_no)
problem opps;// contruct object
throw oops;// throw it
...
...
try
super();
catch (problem& p)
// statements
p将指向oops的副本而不是oops本身。这是件好事,因为函数super()执行完毕后,oops将不复存在。顺便说一句,将引发异常和创建对象组合在一起将更简单:
throw problem();// construct and throw default problem object
可以一次抛出多个异常:
double Argh(double, double) throw(out_of_bounds, bad_exception);
你可能会问,既然throw语句将生成副本,为何代码中使用引用呢?毕竟,将引用作为返回值的通常原因是避免创建副本以提高效率。答案是,引用还有另一个重要特征:基类引用可以执行派生类对象。假设有一组通过继承关联起来的异常类型,则在异常规范中只需列出一个基类引用,它将与任何派生类对象匹配。
catch块排列顺序:
如果有一个异常类继承层次结构,应这样排列catch块:将捕获位于层次结构最下面的异常类的catch语句放在最前面,将捕获基类异常的catch语句放在最后面。
class bad_1 ...;
class bad_2 : public bad_1 ...;
class bad_3 : public bad_2 ...;
...
void duper()
...
if (oh_no)
throw bad_1();
if (rats)
throw bad_2();
if (drat)
throw bad_3();
...
try
duper();
catch(bad_3& be)
// statements
catch(bad_2& be)
// statements
catch(bad_1& be)
// statements
如果将bad_1 &处理程序放在最前面,它将捕获异常bad_1、bad_2和bad_3;通过按相反的顺序排列,bad_3异常将被bad_3 &处理程序所捕获。
假设你编写了一个调用另一个函数的函数,而你并不知道被调用的函数可能引发哪些异常。在这种情况下,仍能够捕获异常,即使不知道异常的类型。方法是使用省略号来表示异常类型,从而捕获任何异常:
catch (...) // statements // catch any type exception
可以将上述捕获所有异常的catch块放在最后面,这有点类似于switch语句中的default:
try
duper();
catch(bad_3& be)
// statements
catch(bad_2& be)
// statements
catch(bad_1& be)
// statements
catch (...) // catch whatever is left
// statements
可以创建捕获对象而不是引用的处理程序。在catch语句中使用基类对象时,将捕获所有的派生类对象,但派生特性将被剥去,因此将使用虚方法的基类版本。
exception类:
<exception>头文件(以前为exception.h或except.h)定义了exception类,C++可以把它用作其他异常类的基类。代码可以引发exception异常,也可以将exception类用作基类。有一个名为what()的虚拟成员函数,它返回一个字符串,该字符串的特征随实现而异。然而,由于这是一个虚方法,因此可以在从exception派生而来的类中重新定义它:
virtual const char* what() const noexcept;
#include <exception>
class bad_hmean : public std::exception
public:
const char* what() return "bad arguments to hmean()";
...
;
class bad_gmean : public std::exception
public:
const char* what() return "bad arguments to gmean()";
...
;
如果不想以不同的方式处理这些派生而来的异常,可以在同一个基类处理程序中捕获它们:
try
...
catch(std::exception& e)
cout << e.what() << endl;
...
否则,可以分别捕获它们。
stdexcept异常类:
头文件<stdexcept>定义了其他几个异常类。
它分为逻辑错误和运行时错误两大类,它们的类层次结构如下:
————————————————————————
逻辑异常:logic_error描述了典型的逻辑错误。
数学函数有定义域(domain)和值域(range)。定义域由参数的可能取值组成,值域由函数可能的返回值组成。例如:如果你编写一个函数,该函数将一个参数传递给函数std::sin(),则可以让该函数在参数不在定义域−1到+1之间时引发domain_error异常。
异常invalid_argument指出给函数传递了一个意料外的值。例如:如果函数希望接受一个这样的字符串:其中每个字符要么是‘0’要么是‘1’,则当传递的字符串中包含其他字符时,该函数将引发invalid_argument异常。
异常length_error用于指出没有足够的空间来执行所需的操作。例如:string类的append()方法在合并得到的字符串长度超过最大允许长度时,将引发length_error异常。
异常out_of_bounds通常用于指示索引错误。例如:你可以定义一个类似于数组的类,其operator() [ ]在使用的索引无效时引发out_of_bounds异常。
运行时异常:runtime_error异常系列描述了可能在运行期间发生但难以预计和防范的错误。
下溢(underflow)错误在浮点数计算中。一般而言,存在浮点类型可以表示的最小非零值,计算结果比这个值还小时将导致下溢错误。整型和浮点型都可能发生上溢错误,当计算结果超过了某种类型能够表示的最大数量级时,将发生上溢错误。计算结果可能不再函数允许的范围之内,但没有发生上溢或下溢错误,在这种情况下,可以使用range_error异常。
一般而言,logic_error系列异常表明存在可以通过编程修复的问题,而runtime_error系列异常表明存在无法避免的问题。
bad_alloc异常和new:
对于使用new导致的内存分配问题,C++的最新处理方式是让new引发bad_alloc异常。头文件<new>包含bad_alloc类的声明,它是从exception类公有派生而来的。但在以前,当无法分配请求的内存量时,new返回一个空指针。
// newexcp.cpp -- the bad_alloc exception
#include <iostream>
#include <new>
#include <cstdlib> // for exit(), EXIT_FAILURE
using namespace std;
struct Big
double stuff[20000];
;
int main()
Big* pb;
try
cout << "Trying to get a big block of memory:\\n";
pb = new Big[10000]; // 1,600,000,000 bytes
cout << "Got past the new request:\\n";
catch (bad_alloc& ba)
cout << "Caught the exception!\\n";
cout << ba.what() << endl;
exit(EXIT_FAILURE);
cout << "Memory successfully allocated\\n";
pb[0].stuff[0] = 4;
cout << pb[0].stuff[0] << endl;
delete[] pb;
return 0;
很多代码都是在new在失败时返回空指针时编写的。为处理new的变化,有些编译器提供了一个标记(开关),让用户选择所需的行为。当前,C++标准提供了一种在失败时返回空指针的new,其用法如下:
int* pi = new (std::nothrow) int;
int* pa = new (std::nowthrow) int[500];
使用这种new,可将上述程序核心代码改为如下:
Big* pb;
...
pb = new (std::nothrow) Big[10000];
if (pb == 0)
cout << "Could not allocate memory. Bye.\\n";
exit(EXIT_FAILURE);
以上是关于❥关于C++之异常的主要内容,如果未能解决你的问题,请参考以下文章