当前位置 博文首页 > C++ 异常机制(上)

    C++ 异常机制(上)

    作者:东南亚季风 时间:2021-01-16 20:05

    目录
    • 一、概念
    • 二、异常的好处
    • 三、基本语法
    • 四、栈解旋
    • 五、异常接口声明
    • 六、异常对象的内存模型
    • 七、异常对象的生命周期

    一、概念

    异常:存在于运行时的反常行为,这些行为超过了函数的正常的功能范围。

    异常处理:处理程序中的错误,异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持。

    在C++中,异常处理包括:

    • throw表达式,表示遇到了无法处理的问题
    • try语句块,处理异常;以关键字try开始,一个或多个catch结束
    • 一套异常类,用于在throw表达式和相关的catch子句之间传递异常的信息。

    二、异常的好处

    1. 整性返回值没有语义信息,而异常包含语义信息,有时从类名便可看出。
    2. 异常作为一个类,有自己的成员,可以传递足够的信息。
    3. 函数的返回值可以忽略,异常不可以忽略,可以使程序更加健壮。

    三、基本语法

    #include<iostream>
    using namespace std;
    
    //异常基本语法
    
    int divide(int x ,int y){
    	if (y == 0){
    		throw y;  //抛异常
    	}
    	return x / y;
    }
    void test01(){
    
    	//试着去捕获异常
    	try{
    		divide(10, 0);
    	}
    	catch (int e){ //异常时根据类型进行匹配
    		cout << "除数为" << e << "!" << endl;
    	}	
    }
    
    
    void CallDivide(int x,int y){	
    	divide(x, y);
    }
    //a() -> b() - >c()  -> d(),d()中的异常一层层向上抛到terminate的标准库函数,直到处理为止
    
    void test02(){	
    	try{
    		CallDivide(10,0);
    	}
    	catch (int e){
    		cout << "除数为" << e << endl;
    	}
    }
    
    //C++异常机制跨函数
    //异常必须处理,如果异常抛到顶层还没有处理,程序便会挂掉。
    int main(){
    	
    	//test01();
    	test02();
    }
    

    四、栈解旋

    异常被抛出后,从进入try块起,到异常被抛前,这期间在栈上构造的所有对象,都会被自动析构,析构的顺序与构造的顺序相反,这一过程即为栈解旋

    构造函数没有返回类型,无法通过返回值来报告运行状态,所以通过异常机制来解决构造函数的出错问题。

    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    using namespace std;
    
    class Person{
    public:
    	Person(){
    		cout << "对象构建!" << endl;
    	}
    	~Person(){
    		cout << "对象析构!" << endl;
    	}
    };
    
    int divide(int x,int y){
    	Person p1, p2;
    	if (y == 0){
    		throw y;
    	}
    	return  x / y;
    }
    
    void test01(){
    
    	try{
    		divide(10,0);//栈解旋
    	}
    	catch (int e){
    		cout << "异常捕获!" << endl;
    	}
    }
    
    int main(void)
    {
    	test01();
    	return 0;
    }
    /*
    结果:
        对象构建!
    	对象构建!
    	对象析构!
    	对象析构!
    	异常捕获!
    */
    

    五、异常接口声明

    1. 为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型,例如:
      void func() throw (A, B, C , D); //这个函数func()能够且只能抛出类型A B C D及其子类型的异常。
    2. 如果在函数声明中没有包含异常接口声明,则次函数可以抛掷任何类型的异常,例如:
      void func();
    3. 一个不抛掷任何类型异常的函数可以声明为:
      void func() throw();
    4. 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexpected函数会被调用,该函数默认行为调用terminate函数中止程序
    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    using namespace std;
    
    //这个函数只能抛出int float char三种类型异常,抛出其他的就报错
    void func() throw(int,float,char){
    	throw "abc";
    }
    
    //不能抛出任何异常
    void func02() throw(){
    	throw -1;
    }
    
    //可以抛出任何类型异常
    void func03(){
    }
    
    int main(void)
    {
    	try{
    		func();
    	}
    	catch (char* str){
    		cout << str << endl;
    	}
    	catch (int e){
    		cout << "异常!" << endl;
    	}
    	catch (...){ //捕获所有异常
    		cout << "未知类型异常!" << endl;
    	}
    	return 0;
    }
    
    //结果: 未知类型异常!
    

    六、异常对象的内存模型

    throw的异常是有类型的,可以是数字、字符串、类对象,catch需严格匹配异常类型。

    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    using namespace std;
    
    void func01(){
    	throw 1; //抛出int类型异常
    }
    
    void func02(){
    	throw "exception";
    }
    
    class MyException{
    public:
    	MyException(const char* str){
    		error = new char[strlen(str)+1];
    		strcpy(error, str);
    	}
    	
    	MyException(const MyException& ex){
    		this->error = new char[strlen(ex.error) + 1];
    		strcpy(this->error,ex.error);
    	}
    	MyException& operator=(const MyException& ex){
    		if (this->error != NULL){
    			delete[] this->error;
    			this->error = NULL;
    		}
    		this->error = new char[strlen(ex.error) + 1];
    		strcpy(this->error, ex.error);
    	}
    	
    	void what(){
    		cout << error << endl;
    	}
    	~MyException(){
    		if (error != NULL){
    			delete[] error;
    		}
    	}
    public:
    	char* error;
    };
    
    void fun03(){
    	throw MyException("我刚写异常!");
    }
    
    void test01(){	
    	try{
    		func01();
    	}
    	catch (int e){
    		cout << "int 异常捕获!" << endl;
    	}
    //----------------------------------
    	try{
    		func02();
    	}
    	catch (const char* e){
    		cout << "const char* 异常捕获!" << endl;
    	}
    //----------------------------------
    	try{
    		fun03();
    	}
    	catch (MyException e){
    		e.what();
    	}
    }
    int main(void){	
    	test01();
    	return 0;
    }
    /*
    int 异常捕获!
    const char* 异常捕获!
    我刚写异常!
    */
    

    七、异常对象的生命周期

    1. catch里可以用普通类型元素,引用,指针去接
    2. 普通元素去接,异常对象catch处理完之后就析构
    3. 引用的话,不用调用拷贝构造,异常对象catch处理完之后就析构
    4. 指针接,throw的时候必须用new才能接的到,catch里必须要delete
    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    using namespace std;
    
    class MyException {
    public:
    	MyException() {
    		cout << "构造函数!" << endl;
    	}
    	MyException(const MyException& ex) {
    		cout << "拷贝构造!" << endl;
    	}
    	~MyException() {
    		cout << "析构函数!" << endl;
    	}
    };
    void func() {
    	//throw &(MyException()); //创建匿名对象,调用构造
        //throw new MyException();//用指针接
    	throw MyException();
    }
    void test01();
    int main(void) {
    	test01();
    	return 0;
    }
    
    /*
    void test01();{
    	try {
    		func();
    	}
    	catch (MyException e) {
    		cout << "异常捕获!" << endl;
    	}
    }
    普通类型去接,结果为:
    	构造函数!
    	拷贝构造!
    	异常捕获!
    	析构函数!
    	析构函数!
    */
    
    /*
    void test01();{
    	try {
    		func();
    	}
    	catch (MyException& e) {
    		cout << "异常捕获!" << endl;
    	}
    }
    引用去接,结果为:
    	构造函数!
    	异常捕获!
    	析构函数!
    */
    /*
    void test01();{
    	try {
    		func();
    	}
    	catch (MyException* e) {
    		cout << "异常捕获!" << endl;
    		detele e;
    	}
    }
    指针去接,结果为:
    	构造函数!
    	异常捕获!
    	析构函数!
    */
    
    下一篇:没有了