More Effective C++(条款12:了解“抛出一个异常exception”与“传递一个参数”或“调用一个虚函数”之间的差异)
2018-03-13 10:53
477 查看
函数return值与try块throw exception、函数接收参数与catch字句捕获异常相当类似(不仅声明形式相像,函数参数与exception传递方式都有三种:by
value,by reference ,by pointer(本质上也是by value) )。
尽管函数调用与异常抛出相当类似,“从抛出端传递一个exception到catch子句”和“从函数调用端传递一个实参到被调函数参数”仍然大有不同:
1、调用一个函数,控制权会最终回到调用端(除非函数失败以致无法返回),但是抛出一个exception,控制权不会再回到抛出端
可以简单理解函数调用作用域是“外—里—外”的转换,而异常抛出是“里—外—···”的转换(只是便于理解,实际上这个比方并不正确)
2、异常对象总是会被复制,若以by value方式捕捉,它甚至被复制两次。传递给函数参数的对象则不一定被复制。
无论catch接收的异常是by
reference还是by
value,被抛出的异常对象至少被复制一次:
若抛出的exception是一个局部对象,在栈展开过程一旦该局部对象离开其生存空间,局部对象destructor会被调用,被销毁,因而需要产生一个临时对象保存被throw的异常(若此时是以局部对象本身传递给catch子句,此子句收到的是一个被析构的对象,已经不存在了)。
即使被抛出对象没有被析构危险,复制行为也会发生。(例如:将抛出对象定义为static的,会存在直到程序结束)
“exception objects必定会造成复制行为”解释了“传递参数”和“抛出异常”另一个不同:后者比前者慢。
当对象被复制当作一个exception,复制行为由copy constructor执行,复制动作永远以对象的“静态类型”为本。
“异常对象是其他对象的副本”===>>:
“传递参数”和“抛出异常”另一个不同:函数调用过程中将一个临时对象传递给一个non-const
reference参数是不允许的(条款19),但对exception则属合法。
以by value方式传递函数自变量,是对被传递对象做一个副本,此副本存储于函数参数中--->
以by
value方式传递exception也发生同样事情,故会构造两个副本,即(1)“任何exception都会产生的临时对象”(2)将临时对象复制到catch子句参数。而以by reference方式传递exception不会(引用参数被绑定到传过来的对象的身上,对参数所做的事是施加于传过来的对象身上到的)。
throw by pointer事实上相当于pass by pointer,两者都传递指针副本(不需复制对象)。(不要抛出指向局部对象的指针,因为该局部对象离开其生存空间时会被销毁,catch子句会获得指向已被销毁的对象的指针)
3、“被抛出成为exception”的对象,其被允许的类型转换动作,比“被传递到函数去”的对象少。
函数调用与异常抛出参数匹配规则不同:
如果有多个重载函数,那么选择参数最为匹配的那个,找不到匹配的函数则进行实参的转换,尽量匹配上,有多个相当匹配的函数则发生二义性。也就是说,函数匹配采用“最佳吻合”策略;
异常抛出则不同,catch子句依出现顺序做匹配尝试,一旦找到一个“相对兼容的”类型就视为匹配成功,就算后面有完全匹配的类型也会被无视。也就是说,异常抛出的参数匹配采用“最先吻合”策略。也正是由于这种策略,异常抛出的参数所允许的转换比函数实参匹配所允许的转换要严格得多,只允许以下转换:
1)“继承架构中的类转换”:派生类异常可以被基类参数捕获,因此catch子句出现顺序应该是先派生类再基类
2)非const到const的转换
3)数组转为数组类型的指针
4)其他指针转“无型指针”(void*指针)
4、catch子句以其“出现与源代码的顺序”被编译器检索对比,其中第一个匹配成功便执行;而以某对象调用虚函数时,被选中执行的是那个“与对象类型最佳吻合”的函数,不论它是不是源代码序列的第一个。
value,by reference ,by pointer(本质上也是by value) )。
尽管函数调用与异常抛出相当类似,“从抛出端传递一个exception到catch子句”和“从函数调用端传递一个实参到被调函数参数”仍然大有不同:
1、调用一个函数,控制权会最终回到调用端(除非函数失败以致无法返回),但是抛出一个exception,控制权不会再回到抛出端
可以简单理解函数调用作用域是“外—里—外”的转换,而异常抛出是“里—外—···”的转换(只是便于理解,实际上这个比方并不正确)
2、异常对象总是会被复制,若以by value方式捕捉,它甚至被复制两次。传递给函数参数的对象则不一定被复制。
无论catch接收的异常是by
reference还是by
value,被抛出的异常对象至少被复制一次:
若抛出的exception是一个局部对象,在栈展开过程一旦该局部对象离开其生存空间,局部对象destructor会被调用,被销毁,因而需要产生一个临时对象保存被throw的异常(若此时是以局部对象本身传递给catch子句,此子句收到的是一个被析构的对象,已经不存在了)。
即使被抛出对象没有被析构危险,复制行为也会发生。(例如:将抛出对象定义为static的,会存在直到程序结束)
“exception objects必定会造成复制行为”解释了“传递参数”和“抛出异常”另一个不同:后者比前者慢。
当对象被复制当作一个exception,复制行为由copy constructor执行,复制动作永远以对象的“静态类型”为本。
“异常对象是其他对象的副本”===>>:
catch(Widget& w) { ... throw;//重新抛出当前异常 } catch(Widget& w) { ... throw w;//重新抛出一个新的异常,即当前异常的副本 }
“传递参数”和“抛出异常”另一个不同:函数调用过程中将一个临时对象传递给一个non-const
reference参数是不允许的(条款19),但对exception则属合法。
以by value方式传递函数自变量,是对被传递对象做一个副本,此副本存储于函数参数中--->
以by
value方式传递exception也发生同样事情,故会构造两个副本,即(1)“任何exception都会产生的临时对象”(2)将临时对象复制到catch子句参数。而以by reference方式传递exception不会(引用参数被绑定到传过来的对象的身上,对参数所做的事是施加于传过来的对象身上到的)。
throw by pointer事实上相当于pass by pointer,两者都传递指针副本(不需复制对象)。(不要抛出指向局部对象的指针,因为该局部对象离开其生存空间时会被销毁,catch子句会获得指向已被销毁的对象的指针)
3、“被抛出成为exception”的对象,其被允许的类型转换动作,比“被传递到函数去”的对象少。
函数调用与异常抛出参数匹配规则不同:
如果有多个重载函数,那么选择参数最为匹配的那个,找不到匹配的函数则进行实参的转换,尽量匹配上,有多个相当匹配的函数则发生二义性。也就是说,函数匹配采用“最佳吻合”策略;
异常抛出则不同,catch子句依出现顺序做匹配尝试,一旦找到一个“相对兼容的”类型就视为匹配成功,就算后面有完全匹配的类型也会被无视。也就是说,异常抛出的参数匹配采用“最先吻合”策略。也正是由于这种策略,异常抛出的参数所允许的转换比函数实参匹配所允许的转换要严格得多,只允许以下转换:
1)“继承架构中的类转换”:派生类异常可以被基类参数捕获,因此catch子句出现顺序应该是先派生类再基类
2)非const到const的转换
3)数组转为数组类型的指针
4)其他指针转“无型指针”(void*指针)
4、catch子句以其“出现与源代码的顺序”被编译器检索对比,其中第一个匹配成功便执行;而以某对象调用虚函数时,被选中执行的是那个“与对象类型最佳吻合”的函数,不论它是不是源代码序列的第一个。
相关文章推荐
- More Effective C++ 条款12 了解”抛出一个exception"与“传递一个参数”或“调用一个虚函数”之间的差异
- More Effective C++----(12)理解"抛出一个异常"与"传递一个参数"或"调用一个虚函数"间的差异
- 【M12】了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差异
- C++中理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异(11)---《More Effective C++》
- More Effective C++ 阅读笔记(八)--“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异
- 条款十二:了解“抛出一个异常”与“传递一个参数”之间的差异
- 5.理解“抛出一个异常”,“传递一个参数”和“调用一个虚函数”间的差异
- More Effective C++之Item M12:理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异
- C++中理解“传递参数”和异常之间的差异
- Effective C++——》条款5:了解C++默默编写并调用哪些函数 .
- 读书笔记_Effective_C++_条款二十五: 考虑写出一个不抛出异常的swap函数
- Effective C++ 条款5——了解C++默默编写并调用哪些函数
- More Effective C++(条款15:了解异常处理(exception handling)的成本)
- 读书笔记_Effective_C++_条款二十五: 考虑写出一个不抛出异常的swap函数
- 读书笔记_Effective_C++_条款五:了解C++默默编写并调用哪些函数
- effective c++ 条款05 了解C++默默编写和调用了哪些函数
- Effective C++条款05解读: 了解C++默默编写并调用哪些函数
- C#调用参数为函数指针的API函数 - 以SetUnhandledExceptionFilter为例编写一个全局异常处理程序
- 抛出异常与传参数及调用虚函数之间的区别与联系
- Effective C++——》条款25:考虑写一个不抛出异常的swap函数