从零开始学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 条评论
登录 后参与评论

相关文章

来自专栏大前端_Web

js浮点数加减乘除

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

1053
来自专栏吾爱乐享

java学习之数组元素排序,冒泡排序和选择排序

1054
来自专栏IT可乐

深入理解计算机系统(3.8)------数组分配和访问

  上一篇博客我们讲解了汇编语言中过程(函数)的调用实现。理解数据如何在调用者和被调用者之间传递,以及在被调用者当中局部变量内存的分配以及释放是最重要的。那么这...

19810
来自专栏Hongten

python开发_python中str.format()

1332
来自专栏DannyHoo的专栏

Block

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

982
来自专栏web前端教室

十一国庆节 之 “变量与函数同名时,会输出谁?”

看这样一个题目: b = function c() { a = 1, b = 2, c = 3; console.log(a); //1 ...

17510
来自专栏python3

python 函数

函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

681
来自专栏大闲人柴毛毛

稳扎稳打JavaScript(一)——作用域链内存模型

几个概念 在开始之前,先了解几个概念。 1.1. 作用域 作用域是指当前正在执行的代码能够访问到变量的范围; 每个函数都有各自的作用域,存储函数所有的局部变量...

4318
来自专栏淡定的博客

python入门基础语法总结

923
来自专栏nnngu

018 final 关键字的用途

final关键字的含义 final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了,...

3456

扫码关注云+社区