C++学习之异常处理

  1. 传统C中的错误处理
    1)通过返回值表示错误
    优点:函数调用路径中所有栈对象都可以被右花括号正确的析构,不会内存泄漏.
    缺点:错误处理流程比较复杂,需要逐层返回值判断,代码臃肿.

    #include <iostream>
    #include <cstdio>
    using namespace std;
    class A{
    public:
        A(void){ cout << "A的构造函数" << endl; }
        ~A(void){ cout << "A的构造析构" << endl; }
    };
    int func3(){
        A a;
        FILE* file = fopen("xx.txt","r");
        if(file==NULL){
            cout << "文件打开失败" << endl;
            return -1;
        }
        cout << "func3正常执行" << endl;
        fclose(file);
        return 0;
    }
    int func2(){
        A a;
        if(func3()==-1){
            return -1;
        }
        cout << "func2正常执行" << endl;
        return 0;
    }
    int func1(){
        A a;
        if(func2()==-1){
            return -1;
        }
        cout << "func1正常执行" << endl;
        return 0;
    }
    int main(){
        if(func1()==-1){
            return -1;
        }
        cout << "main正常执行" << endl;
        return 0;
    }
    

    2)通过远程跳转处理错误
    优点:不需要逐层返回值判断,一步到位处理错误处理,代码精炼
    缺点:函数调用路径中的栈对象失去被析构机会,有内存泄漏的风险.

    #include <iostream>
    #include <cstdio>
    #include <csetjmp>//远程跳转
    
    jmp_buf g_env;
    
    using namespace std;
    class A{
    public:
        A(void){ cout << "A的构造函数" << endl; }
        ~A(void){ cout << "A的构造析构" << endl; }
    };
    int func3(){
        A a;
        FILE* file = fopen("xx.txt","r");
        if(file==NULL){
            longjmp(g_env,-1);
        }
        cout << "func3正常执行" << endl;
        fclose(file);
        return 0;
    }
    int func2(){
        A a;
        func3();
        cout << "func2正常执行" << endl;
        return 0;
    }
    int func1(){
        A a;
        func2();
        cout << "func1正常执行" << endl;
        return 0;
    }
    int main(){
        if(setjmp(g_env) == -1){
            cout << "文件打开失败" << endl;
            return -1;
        }
        func1();
        cout << "main正常执行" << endl;
        return 0;
    }
    
  2. C++异常机制
    1)异常抛出
    throw 异常对象;
    注:异常对象可以是基本类型也可以是类类型对象.

    2)异常检测和捕获
    try{
    可能引发异常语句;

    }
    catch(异常类型1){
    针对异常类型1的处理
    }
    catch(异常类型2){
    针对异常类型2的处理
    }

    注:catch子句根据异常对象类型自上而下顺序匹配,而不是最优匹配,因此要把对子类类型异常捕获语句要写在前面,否则将会被基类类型的异常捕获语句提前的截获。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    class FileError{
    public:
        FileError(const string& file,int line)
            :m_file(file),m_line(line){
            cout << "出错位置:" << m_file << "," <<
                m_line << endl;
        }
    private:
        string m_file;
        int m_line;
    };
    class A{
    public:
        A(void){ cout << "A的构造函数" << endl; }
        ~A(void){ cout << "A的构造析构" << endl; }
    };
    int func3(){
        A a;
        FILE* file = fopen("xx.txt","r");
        if(file==NULL){
            //预定义的宏:
            //__FILE__:当前文件名字的字符串
            //__LINE__:当前位置行号(int)
            throw FileError(__FILE__,__LINE__);
            //throw -1;//抛出异常
        }
        cout << "func3正常执行" << endl;
        fclose(file);
        return 0;
    }
    int func2(){
        A a;
        func3();
        cout << "func2正常执行" << endl;
        return 0;
    }
    int func1(){
        A a;
        func2();
        cout << "func1正常执行" << endl;
        return 0;
    }
    int main(){
        try{
            func1();
            cout << "main正常执行" << endl;
        }
        catch(int ex){
            if(ex == -1){
                cout << "文件打开失败" << endl;
                return - 1;
            } 
        }
        catch(FileError& ex){
            cout << "File Open Error" << endl;
            return -1;
        }
        return 0;
    }
    
  3. 函数的异常说明
    1)函数的异常说明用于告诉函数调用者,在该函数执行期间可能会抛出的异常类型.
    返回类型 函数名(形参表) throw(异常类型表) {函数体}
    eg:
    //说明func函数执行时可能会抛出FileError或int类型异常
    void func(void) throw(FileError,int){…}
    2)函数的异常说明是一种承诺,表示该函数所抛出的异常类型不会超出说明范围。但如果抛出异常说明以外的其它类型,则无法被该函数的调用者正常捕获,而是继续向上抛出,最终被系统捕获,导致进程终止
    3)函数异常说明的两种极端形式
    –》不写函数异常说明,表示可以抛出任何异常
    –》空异常说明,“throw()”,表示不会抛出任何异常
    4)如果函数的声明和定义分开写,要保证异常说明类型一致,但是顺序无所谓
    5)如果基类中的虚函数带有异常说明,那么该函数在子类中的覆盖版本不能说明比基类抛出更多异常,否则将会因为“放松throw限定”而导致编译失败

  4. 标准异常类(exception)

    class exception{
     	public:
        exception() throw() { } 
        virtual ~exception() throw() ;
        /*Returns a C-style character string describing 
        	the general cause of the current error.  */  
        virtual const char* what() const throw();
     };
    

     
    利用多态和标准异常类实现更加便捷的异常处理:

    #include <iostream>
    #include <exception>
    using namespace std;
    class FileError:public exception{
    public:
        const char* what(void) const throw(){
            cout << "针对文件错误处理" << endl;
            return "FileError";
        }
    };
    class MermoryError:public exception{
    public:
        const char* what(void) const throw(){
            cout << "针对内存错误处理" << endl;
            return "MermoryError";
        }
    };
    void func(void){
        //throw FileError();
        throw MermoryError();
    }
    int main(void){
        try{
            func();
        }
        catch(exception& ex){
            cout << ex.what() << endl;
        }
        return 0;
    }
    
  5. 构造函数和析构函数中的异常
    1)构造函数可以抛出异常,但是对象将会被不完整构造,这样的对象其析构函数不再会被自动调用执行,因此在构造函数抛出异常之前,需要手动释放之前分配的动态资源.

    #include <iostream>
    using namespace std;
    class A{
    public:
        A(void){
            cout << "动态资源分配" << endl;
            if("error"){
                cout << "动态资源释放" << endl; 
                throw -1;
            }
            cout << "构造函数正常结束" << endl;
        }
        ~A(void){
            cout << "动态资源释放" << endl; 
        }
    };
    int main(void){
        try{
            A a;
        }
        catch(int ex){
            cout << "捕获到异常:" << ex << endl;
        }
        return 0;
    }
    

    2)析构函数不要抛出异常:一方面是抛了异常没人处理(显示调用析构可用try语句处理,但显示调用情况比较复杂,比如,double free等等),另一方面,如果在执行过程中发生了异常,析构也抛出异常,多个异常会导致进程终止,代码如下:

    #include <iostream>
    using namespace std;
    class A{
    public:
        void func(void){
            throw -2;
        }
        ~A(void){
            throw -1;
        }
    };
    int main(void){
        try{
            A a;
            a.func();//throw -2
        }//throw -1
        catch(int ex){
            cout << "捕获到异常:" << ex << endl;
        }
        return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值