8、异常处理机制专题
前言
1)异常是一种程序控制机制,与函数机制独立和互补
函数是一种以栈结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它依附于栈结构,却可以同时设置多个异常类型作为网捕条件,从而以类型匹配在栈机制中跳跃回馈.
2)异常设计目的:
栈机制是一种高度节律性控制机制,面向对象编程却要求对象之间有方向、有目的的控制传动,从一开始,异常就是冲着改变程序控制结构,以适应面向对象程序更有效地工作这个主题,而不是仅为了进行错误处理。
异常设计出来之后,却发现在错误处理方面获得了最大的好处。
8.1 异常处理的基本思想
8.1.1传统错误处理机制
通过函数返回值来处理错误。
8.1.2异常处理的基本思想
1)C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。
2)异常是专门针对抽象编程中的一系列错误处理的,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试,如图
3)异常超脱于函数机制,决定了其对函数的跨越式回跳。
4)异常跨越函数
8.2 C++异常处理的实现
8.2.1异常基本语法
1)若有异常则通过throw操作创建一个异常对象并抛掷。
2)将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。
3)如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。
4) catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。
5)如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。
6)处理不了的异常,可以在catch的最后一个分支,使用throw语法,向上扔。
案例1:被零整除案例
int divide(int x, int y ) { if (y ==0) { throw x; } return x/y; }
void main41() { try { cout << "8/2 = " << divide(8, 2) << endl; cout << "10/0 =" << divide(10, 0) << endl; } catch (int e) { cout << "e" << " is divided by zero!" << endl; } catch(...) { cout << "未知异常" << endl; }
cout << "ok" << endl; system("pause"); return ; } |
案例2:
//抛掷异常的程序段
class A{};
void f(){
if(...) throw A;
}
//捕获并处理的异常程序段
void g(){
try{
f();
}catch(B){
cout<<“exception B\n”;
}
}
int main(){
g();
}
throw A将穿透函数f,g和main,抵达系统的最后一道防线——激发terminate函数.
该函数调用引起运行终止的abort函数.
最后一道防线的函数可以由程序员设置.从而规定其终止前的行为.
修改系统默认行为:
u 可以通过set_terminate函数修改捕捉不住异常的默认处理器,从而使得发生捉不住异常时,被自定义函数处理:
u voidmyTerminate(){cout<<“HereIsMyTerminate\n”;}
u set_terminate(myTerminate);
u set_terminate函数在头文件exception中声明,参数为函数指针void(*)().
案例3:
v 构造函数没有返回类型,无法通过返回值来报告运行状态,所以只通过一种非函数机制的途径,即异常机制,来解决构造函数的出错问题。
7)异常机制与函数机制互不干涉,但捕捉的方式是基于类型匹配。捕捉相当于函数返回类型的匹配,而不是函数参数的匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题
比如:
class A{}; class B{};
int main() { try { int j = 0; double d = 2.3; char str[20] = "Hello"; cout<<"Please input a exception number: "; int a; cin>>a; switch(a) { case 1: throw d; case 2: throw j; case 3: throw str; case 4: throw A(); case 5: throw B(); default: cout<<"No throws here.\n"; } } catch(int) { cout<<"int exception.\n"; } catch(double) { cout<<"double exception.\n"; } catch(char*) { cout<<"char* exception.\n"; } catch(A) { cout<<"class A exception.\n"; } catch(B) { cout<<"class B exception.\n"; } cout<<"That's ok.\n"; system("pause"); }//==================================== |
catch代码块必须出现在try后,并且在try块后可以出现多个catch代码块,以捕捉各种不同类型的抛掷。
异常机制是基于这样的原理:程序运行实质上是数据实体在做一些操作,因此发生异常现象的地方,一定是某个实体出了差错,该实体所对应的数据类型便作为抛掷和捕捉的依据。
8)异常捕捉严格按照类型匹配
u 异常捕捉的类型匹配之苛刻程度可以和模板的类型匹配媲美,它不允许相容类型的隐式转换,比如,抛掷char类型用int型就捕捉不到.例如下列代码不会输出“int exception.”,从而也不会输出“That’s ok.” 因为出现异常后提示退出
int main(){
try{
throw‘H’;
}catch(int){
cout<<"int exception.\n";
}
cout<<"That's ok.\n";
}
#include <iostream>
using namespace std;
//1 基本语法
//2 发生异常之后,是跨函数 :
//3 接受异常以后 可以不处理 再抛出异常
//4 catch异常的时 按照类型进行catch
//5 异常捕捉严格按照类型匹配
void main01()
{
try
{
throw 'Z';
}
catch (int e)
{
cout << "捕获int类型异常" << endl;
}
catch (...)
{
cout << " 未知 类型异常" << endl;
}
system("pause");
}
void divide(int x,int y)
{
if (y == 0)
{
throw x;//抛出y=0异常
}
cout << "divide结果:" << x / y << endl;
}
void myDivide(int x, int y)
{
divide(x, y);
}
void main()
{
try
{
//divide(100, 2);
//divide(100, 0);
myDivide(100, 0);
}
catch (int e)
{
cout << e << "被零除" << endl;
}
catch (...)
{
cout << "其他未知类型异常 " << endl;
}
cout << "hello..." << endl;
system("pause");
return;
}
8.2.2栈解旋(unwinding)
异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋(unwinding)。
class MyException {};
class Test { public: Test(int a=0, int b=0) { this->a = a; this->b = b; cout << "Test 构造函数执行" << "a:" << a << " b: " << b << endl; } void printT() { cout << "a:" << a << " b: " << b << endl; } ~Test() { cout << "Test 析构函数执行" << "a:" << a << " b: " << b << endl; } private: int a; int b; };
void myFunc() throw (MyException) { Test t1; Test t2;
cout << "定义了两个栈变量,异常抛出后测试栈变量的如何被析构" << endl;
throw MyException(); }
void main() { //异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象, //都会被自动析构。析构的顺序与构造的顺序相反。 //这一过程称为栈的解旋(unwinding) try { myFunc(); } //catch(MyException &e) //这里不能访问异常对象 catch(MyException ) //这里不能访问异常对象 { cout << "接收到MyException类型异常" << endl; } catch(...) { cout << "未知类型异常" << endl; }
system("pause"); return ; }
#include <iostream>using namespace std; //传统的错误处理机制 //throw int类型异常 void my_strcpy1(char *to, char *from) { if (from == NULL) { throw 1; } if (to == NULL) { throw 2; } //copy是的 场景检查 if (*from == 'a') { throw 3; //copy时出错 } while (*from != '\0') { *to = *from; to++; from++; } *to = '\0'; } //传统的错误处理机制 //throw char*类型异常 void my_strcpy2(char *to, char *from) { if (from == NULL) { throw "源buf出错"; } if (to == NULL) { throw "目的buf出错"; } //copy是的 场景检查 if (*from == 'a') { throw "copy过程出错"; //copy时出错 } while (*from != '\0') { *to = *from; to++; from++; } *to = '\0'; } class BadSrcType {}; class BadDestType {}; class BadProcessType { public: BadProcessType() { cout << "BadProcessType构造函数do \n"; } BadProcessType(const BadProcessType &obj) { cout << "BadProcessType copy构造函数do \n"; } ~BadProcessType() { cout << "BadProcessType析构函数do \n"; } }; //传统的错误处理机制 //throw 类对象 类型异常 void my_strcpy3(char *to, char *from) { if (from == NULL) { throw BadSrcType(); } if (to == NULL) { throw BadDestType(); } //copy是的 场景检查 if (*from == 'a') { printf("开始 BadProcessType类型异常 \n"); throw BadProcessType(); //会不会产生一个匿名对象? } if (*from == 'b') { throw &(BadProcessType()); //会不会产生一个匿名对象? } if (*from == 'c') { throw new BadProcessType; //会不会产生一个匿名对象? } while (*from != '\0') { *to = *from; to++; from++; } *to = '\0'; } void main01() { int ret = 0; char buf1[] = "cbbcdefg"; char buf2[1024] = { 0 }; try { //my_strcpy1(buf2, buf1); //my_strcpy2(buf2, buf1); my_strcpy3(buf2, buf1); } catch (int e) //e可以写 也可以不写 { cout << e << " int类型异常" << endl; } catch (char *e) { cout << e << " char* 类型异常" << endl; } //--- catch (BadSrcType e) { cout << " BadSrcType 类型异常" << endl; } catch (BadDestType e) { cout << " BadDestType 类型异常" << endl; } //结论1: 如果 接受异常的时候 使用一个异常变量,则copy构造异常变量. /* catch( BadProcessType e) //是把匿名对象copy给e 还是e还是那个匿名对象 { cout << " BadProcessType 类型异常" << endl; } */ //结论2: 使用引用的话 会使用throw时候的那个对象 //catch( BadProcessType &e) //是把匿名对象copy给e 还是e还是那个匿名对象 //{ // cout << " BadProcessType 类型异常" << endl; //} //结论3: 指针可以和引用/元素写在一块 但是引用/元素不能写在一块 catch (BadProcessType *e) //是把匿名对象copy给e 还是e还是那个匿名对象 { cout << " BadProcessType 类型异常" << endl; delete e; } //结论4: 类对象时, 使用引用比较合适 // -- catch (...) { cout << "未知 类型异常" << endl; } cout << "hello..." << endl; system("pause"); return; } //传统的错误处理机制 int my_strcpy(char *to, char *from) { if (from == NULL) { return 1; } if (to == NULL) { return 2; } //copy是的 场景检查 if (*from == 'a') { return 3; //copy时出错 } while (*from != '\0') { *to = *from; to++; from++; } *to = '\0'; return 0; } void main() { int ret = 0; char buf1[] = "zbcdefg"; char buf2[1024] = { 0 }; ret = my_strcpy(buf2, buf1); if (ret != 0) { switch (ret) { case 1: printf("源buf出错!\n"); break; case 2: printf("目的buf出错!\n"); break; case 3: printf("copy过程出错!\n"); break; default: printf("未知错误!\n"); break; } } printf("buf2:%s \n", buf2); cout << "hello..." << endl; system("pause"); return; } |
#include <iostream>
using namespace std;
class MyArray
{
public:
MyArray(int len);
~MyArray();
public:
int & operator[](int index);
int getLen();
class eSize
{
public:
eSize(int size)
{
m_size = size;
}
virtual void printErr()
{
cout << "size:" << m_size << " ";
}
protected:
int m_size;
};
class eNegative : public eSize
{
public:
eNegative(int size) : eSize(size)
{
;
}
virtual void printErr()
{
cout << "eNegative 类型 size:" << m_size << " ";
}
};
class eZero : public eSize
{
public:
eZero(int size) : eSize(size)
{
;
}
virtual void printErr()
{
cout << "eZero 类型 size:" << m_size << " ";
}
};
class eTooBig : public eSize
{
public:
eTooBig(int size) : eSize(size)
{
;
}
virtual void printErr()
{
cout << "eTooBig 类型 size:" << m_size << " ";
}
};
class eTooSmall : public eSize
{
public:
eTooSmall(int size) : eSize(size)
{
;
}
virtual void printErr()
{
cout << "eTooSmall 类型 size:" << m_size << " ";
}
};
private:
int *m_space;
int m_len;
};
MyArray::MyArray(int len)
{
if (len < 0)
{
throw eNegative(len);
}
else if (len == 0)
{
throw eZero(len);
}
else if (len > 1000)
{
throw eTooBig(len);
}
else if (len < 3)
{
throw eTooSmall(len);
}
m_len = len;
m_space = new int[len];
}
MyArray::~MyArray()
{
if (m_space != NULL)
{
delete[] m_space;
m_space = NULL;
m_len = 0;
}
}
int & MyArray::operator[](int index)
{
return m_space[index];
}
int MyArray::getLen()
{
return m_len;
}
void main()
{
try
{
MyArray a(-5);
for (int i = 0; i<a.getLen(); i++)
{
a[i] = i + 1;
printf("%d ", a[i]);
}
}
catch (MyArray::eSize &e)
{
//cout << "len的大小: " << e.eSize();
e.printErr();
}
catch (...)
{
}
cout << "hello..." << endl;
system("pause");
return;
}
// 不推荐
void main51()
{
try
{
MyArray a(-5);
for (int i = 0; i<a.getLen(); i++)
{
a[i] = i + 1;
printf("%d ", a[i]);
}
}
catch (MyArray::eNegative e)
{
cout << "eNegative 类型异常" << endl;
}
catch (MyArray::eZero e)
{
cout << "eZero 类型异常" << endl;
}
catch (MyArray::eTooBig e)
{
cout << "eTooBig 类型异常" << endl;
}
catch (MyArray::eTooSmall e)
{
cout << "eTooSmall 类型异常" << endl;
}
catch (...)
{
}
cout << "hello..." << endl;
system("pause");
return;
}
// out_of_range
#include "iostream"
using namespace std;
#include <stdexcept> //标准异常库
#include "string"
// out_of_range
class Teacher
{
public:
Teacher(int age)
{
if (age > 100)
{
string s = "年龄太大";
throw out_of_range(s);
}
this->age = age;
}
protected:
private:
int age;
};
void main()
{
try
{
Teacher t1(102);
}
catch (out_of_range e)
{
cout << e.what() << endl;
}
exception e;
system("pause");
}
class MyException : public exception
{
public:
MyException(const char *p)
{
this->m_p = p;
}
virtual const char * what()
{
cout << "MyException: 类型" << m_p << endl;
return m_p;
}
protected:
private:
const char *m_p;
};
void testMyExcept()
{
throw MyException("函数异常");
}
void main01()
{
try
{
testMyExcept();
}
catch (MyException & e)
{
e.what(); //MyException: 类型函数异常
}
catch (...)
{
cout << "未知 类型 " << endl;
}
system("pause");
}
// bad_alloc
#include "iostream"
#include <cmath>
using namespace std;
#include <stdexcept> //标准异常库
#include "string"
double area(double a, double b, double c) throw(invalid_argument)
{
//判断三角形边长是否为正
if (a <= 0 || b <= 0 || c <= 0)
{
throw invalid_argument("the side length should be positive");
}
//判断边长是否满足三角形不等式
if (a+b <= c||b+c<=a||c+a<=b)
{
throw invalid_argument("the side length should fit the triangle inequation");
}
double s = (a + b + c) / 2;
return sqrt(s*(s-a)*(s-b)*(s-c));
}
int main()
{
double a, b, c;
cout << "please input " << endl;
cin >> a >> b >> c;
try
{
double s = area(a,b,c);
cout << "Area:" << s << endl;
}
catch (exception &e)
{
cout << "Error:" << e.what() << endl;
}
system("pause");
return 0;
}