偶然间读到大神的著作,了解到异常处理也是程序设计中非常重要的一块,略有收获,遂记录之,以备查用。现有如下简单代码,vs2010测试通过
1.对象被抛出作为异常时,总会发生拷贝赋值,且临时对象拷贝以所抛出对象的静态类型为准
- // 异常抛出与函数调用的差异.cpp: 主项目文件。
- #include "stdafx.h"
- #include<iostream>
- #include<string>
- using namespace std;
- using namespace System;
- class Widget
- {
- public:
- Widget(int ID):id(ID)
- {
- cout<<"Widget构造函数被调用"<<endl;
- }
- Widget(Widget &w)
- {
- this->id=w.id;
- cout<<"Widget拷贝构造函数被调用"<<endl;
- }
- friend ostream& operator<<(ostream &os,Widget &w);//定义友元函数 重载<<
- ~Widget()
- {
- cout<<"Widget析构函数被调用"<<endl;
- }
- private:
- int id;
- };
- ostream& operator<<(ostream &os,Widget &w)
- {
- os<<w.id;
- return os;
- }
- class SpecialWidget:public Widget
- {
- public:
- SpecialWidget(int id,string Name):Widget(id),name(Name)
- {
- cout<<"SpecialWidget构造函数被调用"<<endl;
- }
- ~SpecialWidget()
- {
- cout<<"SpecialWidget构造函数被调用"<<endl;
- }
- private:
- string name;
- };
- void passAndThrowWidget()
- {
- Widget localWidget(1); //创建局部对象
- cout<<localWidget;
- cout<<endl;
- throw localWidget; //此处的localWidget在函数抛出时析构掉,而为catch制作了一个localWidget的副本
- }
- int main(array<System::String ^> ^args)
- {
- try
- {
- passAndThrowWidget();
- }
- catch(Widget w)//接收localWidget接收的副本,因为此处是传值,会再次制作副本的副本
- {
- cout<<"捕获异常"<<endl;
- }
- system("pause");
- return 0;
- }
vs2010输出结构如下图所示:
由此可见全局函数passAndThrowWidet的调用将导致产生三个构造函数的调用,第一次是局部对象的生成,调用普通构造函数;随后局部对象调用拷贝构造制作副本对象;再然后catch使用传值,又一次调用拷贝构造函数。总共三次构造,三次析构。
如果将catch中的形参改为引用,势必会减少第三次的拷贝函数调用,其该后运行效果图如下:
现在我们来修改一下passAndThrowWidget函数:
- void processAndThrowWidget()
- {
- SpecialWidget localSpecialWidge(45,"SpecialWidget");//构造派生类对象
- cout<<localWidget<<endl; //调用友元全局函数
- Widget &w=localSpecialWidge; //使用派生类对象初始化基类引用
- throw w; //w虽然是派生类的引用,但制作临时对象时,以静态类型为准,故此产生基类拷贝构造调用
- }
- int main()
- {
- try
{
passAndThrowWidget();
}
catch(Widget &w)//接收localWidget接收的副本,因为此处是传值,会再次制作副本的副本
{
cout<<"捕获异常"<<endl;
} }
此次运行结果如下图所示:
2.throw抛出的类型和catch接收类型的匹配问题(只存在两种类型的自动转化匹配,继承和无形指针)
现有如下代码:
- #include"stdafx.h"
- #include<iostream>
- using namespace std;
- void f(int x)
- {
- cout<<"全局函数f调用"<<endl;
- throw x;//抛出×××异常
- }
- void main()
- {
- try
- {
- f(3);
- }
- catch(double)
- {
- cout<<"throw抛出的int类型不会被catch的double类型捕获"<<endl;
- }
- catch(int)
- {
- cout<<"捕获"<<endl;
- }
- system("pause");
- }
运行效果图如下:
当f抛出异常时,会匹配int型的catch,如果将catch(int)后继部分去掉,程序会发生异常终止,即int型的异常未能向函数调用那样存在类型转换问题。
但是当涉及到继承和无形指针时,throw,catch存在类型的自动转换。此时catch中的基类型(无论是引用,指针或是传值)都能够捕获throw各种派生类型的异常。
其次,catch(const void *)无型指针可以不过throw出来的各种类型指针。
最后,一个try块后可以跟多个catch块,catch的异常捕获遵循的原则是最先吻合,不同于虚函数中的最佳吻合。
总结:第一,异常对象总是会被复制(基于抛出对象的静态类型),如果以传值方式捕获,其甚至被复制两次。第二,被抛出成为异常的对象,其被允许的类型转换动作只针对继承和无形指针两种情况。第三,catch捕获遵循最先吻合原则。
转载于:https://blog.51cto.com/freetoskey/980741