1. C语言异常处理:
- 使用整形的返回值标识错误。
- 使用errno宏记录错误。
缺陷:
1.有些函数返回1成功,有些返回0成功,有些需要判断指针为 NULL.显得混乱
2.函数的返回值返回错误代码不能携带其他信息(可通过指针)
2.C++
1 .异常处理机制的优势:(可能Java python也一样)
- 函数返回值可以忽略,异常不可以。当出现异常,没有用被程序捕获时,程序就会终止。
- 异常可以包含语义信息,如异常类名。更直观
- 异常是一个类,其成员可以传递大量信息。
- 异常处理可以在调用跳级。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求你在每一级函数中都要进行处理。而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每级函数都处理。
int division(int a,int b)
{
if (b == 0){
return -1;
}
return a / b;
}
2.引发
当隔代多次调用此函数,由于原函数没有异常处理,导致调用者也无法捕获异常。应该是原函数和调用都可以捕获异常,当自己不处理的时候可以让(抛)上次处理。
修改代码:
int division(int a,int b)
{
if (b == 0){
throw 0;
}
return a / b;
}
当A在调用时://由于没有处理异常,程序直接中断执行
int A_tets(int a,int b){
return ret = division(a,b) + 100; //当b为0
}
当B在调用时,
int B_test()
{
int a = 10;
int b = 0;
int ret = 0;
try{
ret = A_test(a,b);
//此时由于A_test没有抛出异常,导致当前函数捕获不了异常
}
catch(int e){
cout<<"一个数除以了:"<< e<<endl;
}
return ret;
}
3.总结
- 若有异常则通过throw操作创建一个异常对象并抛出。
- 将可能抛出异常的程序段放到try块之中。
- 如果在try段执行期间没有引起异常,那么跟在try后面的catch字句就不会执行。
- catch子句会根据出现的先后顺序被检查,匹配的catch语句捕获并处理异常(或继续抛出异常)
- 如果匹配的处理未找到,则运行函数terminate将自动被调用,其缺省功能调用abort终止程序。
- 处理不了的异常,可以在catch的最后一个分支,使用throw,向上抛。
c++异常处理使得异常的引发和异常的处理不必在一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以在适当的位置设计对不同类型异常的处理。
4.捕获方式:
通过异常严格类型匹配,但是与函数匹配机制互不相关。
当 threw 10:threw 'a':threw "ssssss";threw string("ex"); -->
try{
test_threw();
//根据threw的数据类型执行对应的catch()
}
catch (int){
cout << "抛出Int类型异常!" << endl;
}
catch (char a){
cout << "抛出char类型异常!" << endl;
}
catch (char *){
cout << "抛出char*类型异常!" << endl;
}
catch (std::string){
cout << "抛出std::string类型异常!" << endl;
}
5. 当抛出异常时会发生栈解旋:
异常被抛出后,从进入try块起,到异常被抛掷前,
这期间在栈上构造的所有对象,都会被自动析构。
析构的顺序与构造的顺序相反,这一过程称为栈的解旋(unwinding).
void Testthrew()
obj o1("aaa");
obj o2("bbb");
obj o3("ccc");
//抛出异常
string ex = "error";
throw ex;
}
ccc----析构
bbb----析构
aaa----析构
6.异常接口声明
- 为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型,例如:void func()throw(A,B,C);这个函数func能够且只能抛出类型A,B,C及其子类型异常
- 如果在函数声明中没有包含异常接口声明,则此函数可以抛任何类型的异常,例如:void func()
- 一个不抛任何类型异常的函数可声明为:void func() throw()
- 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用,该函数默认行为调用terminate函数中断程序。
//可以抛出所有类型异常
void test_throw(){
...
throw ‘a';
}
//可以抛出指定类型异常
void test_throw() throw(int ,char,char*,std::string)
{
...
string exception = "error!";
throw exception;
}
//不允许
void test_throw() throw()
{
...
string exception = "error!";
throw exception;
unexcepted函数会被调用,该函数默认行为
调用terminate函数中断程序。
}
7. 异常变量生命周期
- throw的异常是有类型的,可以是数字、字符串、类对象。
- throw的异常是有类型的,catch需严格匹配异常类型。
class MyException
{
public:
MyException() {
cout << "异常变量构造" << endl;
};
MyException(const MyException & e){
cout << "拷贝构造" << endl;
}
~MyException(){
cout << "异常变量析构" << endl;
}
};
void test_throw()
{
throw new MyException(); //test1 2都用 throw MyExecption();
}
void test01()
{
try{
test_throw();
}
catch (MyException e){
cout << "捕获 异常" << endl;
}
}
void test02()
{
try{
DoWork();
}
catch (MyException &e){
cout << "捕获 异常" << endl;
}
}
void test03()
{
try{
DoWork();
}
catch (MyException *e){
cout << "捕获 异常" << endl;
delete e;
}
}
前两个报错执行终止
terminate called after throwing an instance of 'MyException*'
Aborted (core dumped)
第三个正常
8.异常的多态
//异常基类
class BaseException{
public:
virtual void printError(){};
};
//空指针异常
class NullPointerException : public BaseException{
public:
virtual void printError(){
cout << "空指针异常!" << endl;
}
};
//越界异常
class OutOfRangeException : public BaseException{
public:
virtual void printError(){
cout << "越界异常!" << endl;
}
};
void doWork(){
throw NullPointerException();
}
void test()
{
try{
doWork();
}
catch (BaseException& ex){
ex.printError();
delete ex;
}
}
9. 标准库中的异常类:
标准异常类的成员:
① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。
② logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述
③ 所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。
#include<stdexcept>
class Person{
public:
Person(int age){
if (age < 0 || age > 150){
throw out_of_range("年龄应该在0-150岁之间!");
}
}
public:
int mAge;
};
int main(){
try{
Person p(151);
}
catch (out_of_range& ex){
cout << ex.what() << endl;
}
}
10.自定义异常类:
继承标准异常,应该重载父类的what函数和虚析构函数。因为栈展开的过程中,要复制异常类型,那么要根据你在类中添加的成员考虑是否提供自己的复制构造函数。
//自定义异常类
class MyOutOfRange:public exception
{
public:
MyOutOfRange(const string errorInfo)
{
this->m_Error = errorInfo;
}
MyOutOfRange(const char * errorInfo)
{
this->m_Error = string( errorInfo);
}
virtual ~MyOutOfRange()
{
}
virtual const char * what() const
{
return this->m_Error.c_str() ;
}
string m_Error;
};
class Person
{
public:
Person(int age)
{
if (age <= 0 || age > 150)
{
//抛出异常 越界
//cout << "越界" << endl;
//throw out_of_range("年龄必须在0~150之间");
//throw length_error("长度异常");
throw MyOutOfRange(("我的异常 年龄必须在0~150之间"));
}
else
{
this->m_Age = age;
}
}
int m_Age;
};
void test01()
{
try
{
Person p(151);
}
catch ( out_of_range & e )
{
cout << e.what() << endl;
}
catch (length_error & e)
{
cout << e.what() << endl;
}
catch (MyOutOfRange e)
{
cout << e.what() << endl;
}
}