c++异常机制相比C语言异常处理的优势:
1 函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会终止,这多少会促使程序员开发出来的程序更健壮一点。而如果使用C语言的error宏或者函数返回值,调用者都有可能忘记检查,从而没有对错误进行处理,结果造成程序莫名其面的终止或出现错误的结果。
2 整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能够体现出来。
3 整型返回值缺乏相关的上下文信息。异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。
4 异常处理可以在调用跳级。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求你在每一级函数中都要进行处理。而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每级函数都处理
异常基本语法:
忽略异常代码
#include <iostream>
using namespace std;
int DivZero_A(int a,int b)
{
if (b == 0)
{
throw 0;
}
return a / b;
}
int DivZero_B(int a, int b)
{
int ret = DivZero_A(a,b)+10;由于DivZero_B没有处理异常,导致B结果运算错误
return ret;
}
int DivZero_C()
{
int a = 10;
int b = 0;
#if 1
int ret = DivZero_B(a, b);//没有处理异常,程序直接中断执行,异常不能忽略
#else
try
{
int ret = DivZero_B(a, b);
}
catch (int e)
{
cout << " DivZero_C Call DivZero_B 除数为:" << e << endl;
}
#endif
return 0;
}
int main()
{
DivZero_C();
system("pause");
return 0;
}
运行中断:
把#if 1改为 0代码就处理异常了,程序可以运行,结果如下:
总结:
1若有异常则通过throw操作创建一个异常对象并抛出
2 将可能抛出异常的程序段放到try块之中
3 如果在try段执行期间没有引起异常,那么跟在try后面的catch字句就不会执行
4 catch子句会根据出现的先后顺序被检查,匹配的catch语句捕获并处理异常(或继续抛出异常)
5 如果匹配的处理未找到,则运行函数terminate将自动被调用,其缺省功能调用abort终止程序
5 处理不了的异常,可以在catch的最后一个分支,使用throw,向上抛
c++异常处理使得异常的引发和异常的处理不必在一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以在适当的位置设计对不同类型异常的处理,也就是《C++ primer》中说的:将问题检测和问题处理相分离
c++异常机制和函数机制互不干涉,但是捕捉方式是通过严格类型匹配
#include <iostream>
using namespace std;
#include<string>
void throw_test()
{
cout << "抛出异常开始" << endl;
string S2= "throw string extremely";//抛出char* 类型异常
//throw 'a';//抛出char类型异常
//throw "char* extremely";
throw S2;
}
void try_test()
{
try
{
throw_test();
}
catch (char e)
{
cout << "抛出char类型异常" << endl;
}
catch (char* e)
{
cout << "抛出char*类型异常" << endl;
}
catch (string e)
{
cout << "抛出string类型异常" << endl;
}
//捕获所有异常
catch (...){
cout << "抛出其他类型异常!" << endl;
}
}
int main()
{
try_test();
system("pause");
return 0;
}
运行结果:
栈解旋:异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋(unwinding)
代码如下:#include <iostream>
using namespace std;
#include<string>
class person
{
public:
person(string name)
{
cout << "person构造函数" << endl;
this->name = name;
}
~person()
{
cout << "person析构函数" << endl;
}
public:
string name;
};
void test_person()
{
person p1("one");
person p2("two");
person p3("three");
throw 3;
}
int main()
{
try
{
test_person();
}
catch (int e)
{
cout << "异常被捕获" << endl;
}
system("pause");
return 0;
}
结果如图:抛出之前创建的三个对象全部自动析构,然后才调用异常
c++标准异常
1 logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述
2 所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息
#include<iostream>
using namespace std;
#include<stdexcept>
class person
{
public:
person(int age)
{
if (age<1 || age>30)
{
throw out_of_range("年龄应该在1-30岁之间");//这是超出有效范围的异常类,标准异常类都有一个描述异常信息的有参构造函数
}
this->age = age;
}
public:
int age;
};
void test_stdexcept()
{
try
{
person p(0);
}
catch (out_of_range& ex)
{
cout << ex.what() << endl;//标准异常类的what方法打印异常信息
}
}
int main()
{
test_stdexcept();
system("pause");
return 0;
}
结果打印异常信息
借助标准异常类写一个自己的异常处理类
1exception:所有标准异常类的父类
2当继承标准异常类时,应该重载父类的what函数和虚析构函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<stdexcept>
#include <string>
//自己的异常类
class myout_of_range :public exception
{
public:
myout_of_range(char* str)
{
pError = new char[strlen(str) + 1];
strcpy(pError, str);
}
virtual const char* what()
{
return pError;
}
~myout_of_range()
{
if (pError != NULL)
{
delete pError;
}
}
public:
char* pError;
};
//使用自己的异常类
class person
{
public:
person(int age)
{
if (age<1 || age>30)
{
throw myout_of_range("年龄应该在1-30岁之间");
}
this->age = age;
}
public:
int age;
};
void test_stdexcept()
{
try
{
person p(0);
}
catch (myout_of_range& ex)
{
cout << ex.what() << endl;
}
}
int main()
{
test_stdexcept();
system("pause");
return 0;
}
打印结果: