C++11新特性-异常处理(二)

一、栈解旋(unwinding)——不是新概念

  • 异常被抛出后,从进入try块起,到异常被抛掷前(注意理解!),这期间在栈上的构造的所有对象(指的是,catch中除了除了throw之外的其他对象),都会被自动析构,析构的顺序与构造的顺序相反(很滋自然的,在我们还没有涉及到异常处理的时候,我们从C++的内存管理就知道在{}这个符号中的操作,除了malloc等操作申请堆之外,其他操作都是在栈上操作的,其实这不是新概念!)。这一过程称为栈的解旋(unwinding)

样例代码

#include<bits/stdc++.h>
using namespace std;
class A{};
class B{};

//自定义的异常类型 
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;
        }

        ~Test()
        {
            cout << "Test 析构函数执行" << "a:" << a << " b: " << b << endl;
        }
    private:
        int a;
        int b;
};


//注意下面的throw的写法! 详情,请看“二、异常接口声明”
void myFunc() 
throw (MyException)
{
    Test t1(111,111);
    Test t2(222,222);
    cout << "定义了两个栈变量,异常抛出后测试栈变量的如何被析构" << endl;

    throw MyException();
}

int main()
{
    try  
    {
        myFunc();
    }
    //catch(MyException &e) //这里不能访问异常对象
    catch(MyException ) //这里不能访问异常对象
    {
        cout << "接收到MyException类型异常" << endl;
    }
    catch(...)
    {
        cout << "未知类型异常" << endl;
    }

    return 0;
}

//Test 构造函数执行a:111 b: 111
//Test 构造函数执行a:222 b: 222
//定义了两个栈变量,异常抛出后测试栈变量的如何被析构
//Test 析构函数执行a:222 b: 222
//Test 析构函数执行a:111 b: 111
//接收到MyException类型异常

二、异常接口说明(这个概念得了解,不然看不懂别人写的代码!)

  • 1)为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型,例如:
    void func() 
    throw (A, B, C , D); 
    //这个函数func()能够且只能抛出类型A B C D及其子类型的异常。
  • 2)如果在函数声明中没有包含异常接口声明,则次函数可以抛掷任何类型的异常,例如:
    void func();
  • 3)一个不抛掷任何类型异常的函数可以声明为:
    void func() throw();
  • 4) 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexpected函数会被调用,该函数默认行为调用terminate函数中止程序。

三、异常对象变量的生命周期(异常对象的内存模型)

注意 异常对象的内存模型 。

四、“继承”在异常中的应用(没有搞懂???)

异常的层次结构

  • 异常是类 – 创建自己的异常类
  • 异常派生
  • 异常中的数据:数据成员
  • 按引用传递异常
    • 在异常中使用虚函数

案例:设计一个数组类 MyArray,重载[]操作,
数组初始化时,对数组的个数进行有效检查
1) index<0 抛出异常eNegative
2) index = 0 抛出异常 eZero
3)index>1000抛出异常eTooBig
4)index<10 抛出异常eTooSmall
5)eSize类是以上类的父类,实现有参数构造、并定义virtual void printErr()输出错误。

五、标准程序库异常(标准的!)

Exception,n.例外,规则外的例外

异常处理代码演示
图片说明
图片说明
图片说明
图片说明

1、样例代码1(// out_of_range,表示一个参数不在允许范围之内)

#include<iostream>
#include<stdexcept>  
using namespace std;


class Teacher
{
    public:
        Teacher(int age)  //构造函数, 通过异常机制 处理错误
        {
            if (age > 100)
            {
                throw out_of_range("年龄太大");
            }
            this->age = age;
        }
    private:
        int age;
};

int main()
{
    try
    {
        Teacher t1(102);
    }
    catch (out_of_range e)
    {
        cout << "捕获下列异常:" << endl;
        cout << e.what() << endl;
    }

    return 0;
}

//捕获下列异常:
//年龄太大

2、样例代码2(bad_alloc,用new动态分配空间失败)

#include<bits/stdc++.h>
using namespace std;

class Dog
{
    public:
        Dog()
        {
            parr = new int[1024*1024*100]; //4MB
        }
    private:
        int *parr;
};

int main()
{
    Dog *pDog;

    try
    {
        for(int i=1; i<1024; i++) //40GB!
        {
            pDog = new Dog();
            cout << i << ": new Dog 成功." << endl;
        }
    }
    catch(bad_alloc err)
    {
        cout << "new Dog 失败: " << err.what() << endl;
    }

    return 0;

}

//1: new Dog 成功.
//2: new Dog 成功.
//3: new Dog 成功.
//new Dog 失败: std::bad_alloc

3、样例代码3(invalid_argument,表示向函数传入无效参数)

//exception "标准程序库异常类"的公共基类

#include<iostream>
#include<cmath>
#include<stdexcept> 
using namespace std;


//x
double area(double a,double b,double c) 
throw (invalid_argument)
{
    if(a<=0||b<=0||c<=0)
    {
        throw invalid_argument("边长应该为正");
    }

    if( (a+b)<=c || (b+c)<=a || (a+c)<=b)
    {
        throw invalid_argument("边长不符合条件");
    }


    double s=(a+b+c)/2;
    return sqrt(s*(s-a)*(s-b)*(s-c));

}




int main()
{
    double a,b,c;

    cout<<"输入a,b,c"<<endl; 
    while(cin>>a>>b>>c)
    {
        try
        {
            double s=area(a,b,c);
            cout<<s<<endl;
        }
        catch(exception &e)
        {
            cout<<"Error:"<<e.what()<<endl;
        }

    }    

    return 0;
}

[演示]

输入a,b,c
3 4 5
6
0 5 5
Error:边长应该为正
1 1 2
Error:边长不符合条件

六、补充,C语言(传统处理错误方式)和C++中错误处理对比

0、C语言中传统的处理错误方式

从异常处理和业务处理混合在一起

#include<bits/stdc++.h>
using namespace std;


//文件的二进制copy
int filecopy01(char *filename2, char *filename1 )
{
    FILE *fp1= NULL,  *fp2 = NULL;

    fp1 = fopen(filename1, "rb");
    if (fp1 == NULL)
    {
        return 1;
    }

    fp2 = fopen(filename2, "wb");
    if (fp1 == NULL)
    {
        return 2;
    }

    char buf[256];
    int  readlen, writelen;
    while ( (readlen = fread(buf, 1, 256, fp1)) > 0 ) //如果读到数据,则大于0 
    {
        writelen = fwrite(buf, 1, readlen, fp2);
        if (readlen != readlen)
        {
            return 3;
        }
    }

    fclose(fp1);
    fclose(fp2);
    return 0;
}



int main()
{
    int ret;
    ret = filecopy01("c:/1.txt","c:/2.txt");


    if (ret !=0 )
    {
        switch(ret)
        {
        case 1:
            printf("打开源文件时出错!\n");
            break;
        case 2:
            printf("打开目标文件时出错!\n");
            break;
        case 3:
            printf("拷贝文件时出错!\n");
            break;
        default:
            printf("发生未知错误!\n");
            break;
        }
    }

    return 0;
}

1、C++

  • C++编译器通过throw 来产生对象,C++编译器再执行对应的catch分支,相当于一个函数调用,把实参传递给形参。

//throw int类型变量
//throw 字符串类型
//throw 类类型

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务