一般抛出异常的方式有下面两种:try—catch组合或者直接throw。
一般情况下,throw出去的应该是一个类,像下面我们定义的那个样子
定义之后,就可以在Vector类的运算重载函数中去使用,这也是常用的方式:
值得注意的是,只要throw出了异常,在本行(本括号内的函数体以内)以下的函数不会被调用。
另外,异常也遵循逐级传递,逐级上抛的云原则,像下面这种,本级别无法处理就一直上抛到可以处理的层级
下面是throw的两种表达式,第一种是抛出一个值(包括类),第二种只能用于下一级的catch中
为了捕捉到所有类型的异常,这里可以用catch后面的**...**
代替
当然,像这种(…)的catch形式,因为拿不到throw的对象类型,所以属于无奈之举才会去写,一般都是明确catch的异常类型再进行处理。
异常语句
对于catch语句的选取(选择哪一条catch进行处理),一般有下面的三条原则:
(1)精确匹配异常类型
(2)异常类型的父类
(3)catch(…)
需要注意的是,上面的三条并非优先顺序,实际的选取依据代码的先后顺序来选,比如下面的underFlow错误,是matherr的子类,此时如果catch(underflow&){ ... }
写在matherr的后面,也不会被调用,反而会编译期报错,因为子类异常放在父类异常之后,那么将永远不会被调用。
、
这里,对于异常的限定:
上面的abc()函数后面冒号里面throw括号里面可以放多个异常,意思是对抛出异常进行限定,就是说调用abc函数只可能会抛出MathErr异常,别的都没有可能,但是如果确实抛了其他异常,C++一律抛出Unexpected exception。
关于其他的情况:
throw()里面什么都没有,那就是什么异常都不会抛出,只要抛就是Unexpected exception,而像average那样的,连throw都没有,就是说什么异常都有可能,不声明、不做检验。
特殊地,new不会返回Null,如果出现内存不足,会抛出bad_alloc异常
更特殊地,在构造函数里面抛异常,会出现内存不能清理的问题
像下面的A类,构造函数抛出异常,意味着不能完成构造,也就不能调用析构函数清理内存,此时的buf会变成内存垃圾留在那里
抛出异常的方式有很多,但有些抛出会造成一定的影响,像下面这种直接catch()父类异常的,抛出子类异常,会发生Slicing,就是会丢失一些信息。
下面是第二种,抛出New 的对象,catch一个指针,这种情况下需要及时地delete掉,不然会产生内存垃圾。
第三种是比较倾向的抛出方式,父类虚函数指定子类重载,然后去catch一个引用(不会发生拷贝构造),这种情况下,抛出的子类在堆栈顶部,将来会被覆盖,无需清理