在C++异常处理中,throw可以抛出任何对象,可以是int类型,也可以是class类型,对于catch块而言,有两种选择:
1)catch这个类型,例:
int main()
{
int a=1;
try
{
throw a;
}
catch(int) // 捕获int型异常,无法获知a的内容
{
cout<<"Something wrong happened!"<<endl;
}
return 0;
}
2)catch这个类型的对象,例:
int main()
{
int a=1;
try
{
throw a;
}
catch(int b) // 捕获int型异常,可以获知a的值
{
cout<<"Something wrong happened: "<<b<<endl;
}
return 0;
}这两者的区别很明显,后者可以通过访问异常对象(本例中是b)从而获取异常信息。
throw和catch异常对象很像函数调用和函数定义,因此类似地,也可以catch一个对象的引用,这样可以减少拷贝一个复杂对象的开销,例如:
int main()
{
int a=1;
try
{
throw a;
}
catch(int &b) // 捕获int型异常,减少开销
{
cout<<"Something wrong happened: "<<b<<endl;
}
return 0;
}那么,很自然的想到一个问题:
如果catch的是引用,那么修改该引用,原对象是否会被同步修改?
答案是“否”,下面我们通过一个例子说明在throw…catch这个过程中发生了什么:
首先定义一个异常类用于throw和catch:
class CExceptionTest
{
public:
CExceptionTest(int a=0)
{
cout<<"Default Constructor:"<<a<<endl;
m_a = a;
}
CExceptionTest(CExceptionTest &another)
{
m_a = another.m_a;
cout<<"Copy Constructor:"<<m_a<<endl;
}
CExceptionTest& operator=(int a)
{
m_a=a;
cout<<"Assign Constructor:"<<m_a<<endl;
return *this;
}
int Get()
{
return m_a;
}
void Set(int a)
{
m_a = a;
}
private:
int m_a;
};然后我们测试下如果是pass-by-value传值方式catch异常的时候,会“暗地里”做哪些事情?
CExceptionTest g_a(1); // 默认构造函数
void f1()
{
g_a=2; // 调用赋值构造函数
throw g_a; // 调用拷贝构造函数生成临时对象(tempObj)
}
void f2()
{
try
{
f1();
}
catch(CExceptionTest b) // 调用拷贝构造函数
{
cout<<"Exception happened:"<<b.Get()<<endl;
b.Set(3);
cout<<"Exception happened:"<<b.Get()<<endl;
cout<<"g_a.Get() is "<<g_a.Get()<<endl;
}
}
int main()
{
f2();
return 0;
}编译并执行后,输出为:
1:Default Constructor:1
2:Assign Constructor:2
3:Copy Constructor:2
4:Copy Constructor:2
5:Exception happened:2
6:Exception happened:3
7:g_a.Get() is 2从结果中我们可以看到:
在throw g_a时,会生成一个临时对象,假设为tempObj,此时调用拷贝构造函数,即输出中的第3行,然后catch时,又将该临时对象通过拷贝构造函数生成b,输出中第4行反映了这一点,此时即使我们修改了b的信息,也不会影响到被抛出的对象g_a,这是很正常的。
那么如果我们使用pass-by-ref传引用的方式呢?
我们将f2函数中catch处代码修改为引用后,f2函数形如:
void f2()
{
try
{
f1();
}
catch(CExceptionTest &b) // 引用该临时对象tempObj
{
cout<<"Exception happened:"<<b.Get()<<endl;
b.Set(3);
cout<<"Exception happened:"<<b.Get()<<endl;
cout<<"g_a.Get() is "<<g_a.Get()<<endl;
}
}此时我们编译并执行,输出变为:
1:Default Constructor:1
2:Assign Constructor:2
3:Copy Constructor:2
4:Exception happened:2
5:Exception happened:3
6:g_a.Get() is 2有两点变化:
第一、拷贝函数的调用减少了一次,这说明b对象直接引用了临时对象;
第二、修改b也不会影响被抛出的异常对象g_a,说明throw和catch无论是传值还是传引用都不会影响被抛出的对象。
本文详细探讨了C++中异常处理的基本概念,包括如何使用throw抛出不同类型的异常,以及如何利用catch捕获这些异常。文章通过实例展示了值传递与引用传递的区别,并解释了在不同情况下如何高效地处理异常。
2万+

被折叠的 条评论
为什么被折叠?



