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

相关文章

来自专栏菜鸟前端工程师

JavaScript学习笔记022-原型链0原型继承0对象的深浅拷贝extends

521
来自专栏小狼的世界

Javascript设计模式学习(二)封装续

有一个避免其他程序员无意间写出重名函数的办法,在你想作为私有属性或者私有方法的命名前加一个下划线,像这样 this._name = name;,这样虽然不能避免...

1138
来自专栏xingoo, 一个梦想做发明家的程序员

函数声明后面的const用法

void function() const{} 通常我们会看到一些函数声明后面会跟着一个const,这个const是做什么的呢? 看一下下面的例子,就知道了。直...

1875
来自专栏绿巨人专栏

TypeScript中的怪语法

1043
来自专栏小狼的世界

如何向回调函数中传入其他参数

最近写JS经常会因为向回调函数中传参而头疼,今天总结一下向回调函数中传参的方法,以后的应用中就不用在到处去找了。

491
来自专栏C语言C++游戏编程

轻松学习C语言编程之函数知识详解

函数是一组一起执行任务的语句。每个C程序至少有一个函数,即main,所有最简单的程序都可以定义其他函数。您可以将代码划分为单独的函数。如何在不同的函数之间划分代...

692
来自专栏Java 源码分析

内部类的作用

一、 作用 内部类可以很好的实现隐藏,一般的非内部类,是不允许有 private 与protected权限的,但内部类可以加上这几个修饰词。 内部类拥有外围...

2423
来自专栏Java编程

Java泛型详解

Java泛型(generics)是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter)。声明的类型参数在使用时用具...

6400
来自专栏绿巨人专栏

TypeScript中的怪语法

3315
来自专栏vue学习

underscore源码解析1

Math.random()方法返回大于0小于1的一个随机数。 Math.floor()方法执行向下舍入,即它总是将数值向下舍入为最接近的整数。

641

扫码关注云+社区