从零开始学C++之异常(三):异常与继承、异常与指针、异常规格说明

 一、异常与继承

如果异常类型为C++的类,并且该类有其基类,则应该将派生类的错误处理程序放在前面,基类的错误处理程序放在后面

#include <iostream>
#include <string>

using namespace std;

class MyException
{
public:
    MyException(const char *message)
        : message_(message)
    {
        cout << "MyException ..." << endl;
    }
    MyException(const MyException &other) : message_(other.message_)
    {
        cout << "Copy MyException ..." << endl;
    }
    virtual ~MyException()
    {
        cout << "~MyException ..." << endl;
    }

    const char *what() const
    {
        return message_.c_str();
    }
private:
    string message_;
};

class MyExceptionD : public MyException
{
public:
    MyExceptionD(const char *message)
        : MyException(message)
    {
        cout << "MyExceptionD ..." << endl;
    }
    MyExceptionD(const MyExceptionD &other)
        : MyException(other)
    {
        cout << "Copy MyExceptionD ..." << endl;
    }
    ~MyExceptionD()
    {
        cout << "~MyExceptionD ..." << endl;
    }
};

int main(void)
{
    try
    {
        MyExceptionD e("test exception");
        throw e;
    }
    catch (MyExceptionD &e)
    {
        cout << "catch MyExceptionD ..." << endl;
        cout << e.what() << endl;
    }
    catch (MyException &e)
    {
        cout << "catch MyException ..." << endl;
        cout << e.what() << endl;
    }

    return 0;
}

派生类的异常能够被基类所捕获,且前面的异常处理程序能够匹配的话首先catch,如果把基类的放在最前面,而且不是引用的形式,如 catch (MyException e); 那么将会被这个所catch 到,而且在构造e 的过程会有object slicing 的问题。

二、异常与指针

抛出指针通常是一个坏主意,因为抛出指针要求在对应处理代码存在的任意地方都存在指针所指向的对象(注意此时throw抛出时复制的是指针本身,不会去复制指针指向的内容)

int main(void)
{
    try
    {
        //MyExceptionD e("test exception");
        //throw &e;
        throw new MyExceptionD("test exception");
    }
    /*catch (void* e)
    {
        cout<<"catch void* ..."<<endl;
        cout<<((MyExceptionD*)e)->what()<<endl;
        delete (MyExceptionD*)e;
    }*/
    catch (MyExceptionD *e)
    {
        cout << "catch MyExceptionD ..." << endl;
        cout << e->what() << endl;
        delete e;
    }
    catch (MyException &e)
    {
        cout << "catch MyException ..." << endl;
        cout << e.what() << endl;
    }

    return 0;
}

其中MyException, MyExeptionD类如上所示,现在MyExeptionD 对象是在堆上分配的,所以在catch 的时候还没释放,还可以访问到e->what(); 但需要自己在catch 末尾delete e; 假设将 throw new MyExceptionD("test exception"); 换成MyExceptionD e("test exception");throw &e;

即抛出局部对象的指针,由于在catch 时MyExeptionD 对象已经被析构了,所以访问不到e->what(); 即e是空悬指针。

还有一点是,任何类型的指针都能被void* 指针所捕获,如果将catch (void* e);注释打开,那么由于排在前面,异常首先将被它所捕获。

三、异常规格说明

1、异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的声明中列出这个函数可能抛掷的所有异常类型。 例如:

void fun() throw(A,B,C,D);

2、若无异常接口声明,则此函数可以抛掷任何类型的异常。

3、不抛掷任何类型异常的函数声明如下: void fun() throw();

void fun(int n) throw (int, MyException, MyExceptionD)
{
    if (n == 1)
    {
        throw 1;
    }
    else if (n == 2)
    {
        throw MyException("test Exception");
    }
    else if (n == 3)
    {
        throw MyExceptionD("test ExceptionD");
    }

}

void fun2() throw()
{

}

int main(void)
{
    try
    {
        fun(2);
    }

    catch (int n)
    {
        cout << "catch int ..." << endl;
        cout << "n=" << n << endl;
    }
    catch (MyExceptionD &e)
    {
        cout << "catch MyExceptionD ..." << endl;
        cout << e.what() << endl;
    }
    catch (MyException &e)
    {
        cout << "catch MyException ..." << endl;
        cout << e.what() << endl;
    }

    return 0;
}

实际上编译会产生警告:

warning C4290: 忽略 C++ 异常规范,但指示函数不是 __declspec(nothrow)

就是说VC++编译器现在还不怎么支持异常规格说明,举个例子说,void fun(int n) throw (int, MyException, MyExceptionD); 没有声明double 类型的异常,但在函数内throw 1.0;  在外部catch (double) 还是会成功。

四、C++标准库异常层次

比如dynamic_cast 执行错误会产生bad_cast 异常,new 分配内存错误会产生bad_alloc 异常,其实这些异常类都继承自exception类,但内部的实现都

没有有效的代码,只是用来标识当前程序产生了哪种类型的异常而已。

参考:

C++ primer 第四版 Effective C++ 3rd C++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏HTML5学堂

伪数组

HTML5学堂:熟悉JavaScript的人对document.getElementsByTagName再熟悉不过,对arguments也多有耳闻,我们时常针对...

2654
来自专栏C语言及其他语言

【每日一题】问题 1195: 去掉双斜杠注释

题目描述 将C程序代码中的双斜杠注释去掉。 输入 输入数据中含有一些符合C++语法的代码行(每行代码不超过200个字符)。需要说明的是,...

2659
来自专栏全沾开发(huā)

学习zepto.js(对象方法)[5]

学习zepto.js(对象方法)[5] clone: 该方法不接收任何参数,会返回对象中的所有元素集合,但不会对象绑定的事件. var...

32011
来自专栏令仔很忙

javascript变量:全局?还是局部?这个得注意

        如果有Var,在函数内部声明变量是局部变量,如下例,读取不到name的数据。

693
来自专栏闻道于事

JavaScript函数认识,Js中的常见函数

JavaScript函数: 也称为方法,用来存储一块代码,需要的时候调用。 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。 函数需要包含四要素:返...

2788
来自专栏编程坑太多

JS实现运算符重载

1442
来自专栏C语言及其他语言

【每日一题】问题 1195: 去掉双斜杠注释

输入数据中含有一些符合C++语法的代码行(每行代码不超过200个字符)。需要说明的是,为了方便编程,规定双斜杠注释内容不含有双引号,源程序中没空行。

752
来自专栏mukekeheart的iOS之旅

《JavaScript高级程序设计》学习笔记(1)

欢迎关注本人的微信公众号“前端小填填”,专注前端技术的基础和项目开发的学习。 首先,我将从《JavaScript高级程序设计》这本JavaScript学习者必看...

2614
来自专栏代码世界

Python 中格式化字符串 % 和 format 两种方法之间的区别

  Python2.6引入了 format 格式化字符串的方法,现在格式化字符串有两种方法,就是 % 和 format ,具体这两种方法有什么区别呢?请看以下解...

4378
来自专栏烂笔头

Python正则表达式:最短匹配

目录[-] 最短匹配应用于:假如有一段文本,你只想匹配最短的可能,而不是最长。 例子 比如有一段html片段,<a>this is first label<...

3327

扫码关注云+社区