分类:
2010-11-22 14:54:08
#include
#include
using namespace std;
class MyException : public exception
{
public:
MyException(const char* astrDesc)
{
mstrDesc = astrDesc;
}
MyException(const MyException& aoOrig)
{
cout <<"Copy Constructor MyException" < mstrDesc = aoOrig.mstrDesc;
}
MyException& operator=(const MyException& aoOrig)
{
cout <<"Copy Operator MyException" < if(&aoOrig == this)
{
return *this;
}
mstrDesc = aoOrig.mstrDesc;
return *this;
}
~MyException()
{
cout <<"~MyException" < }
string mstrDesc;
};
void exceptionFun()
{
try
{
throw MyException("A My Exception");
}
catch(MyException e)
{
cout < e.mstrDesc = "Changed exception.";
throw;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
try
{
exceptionFun();
}
catch(MyException e)
{
cout < throw;
}
return 0;
}
输出:
Copy Constructor MyException
A My Exception In exceptionFun.
~MyException
Copy Constructor MyException
A My Exception Out exceptionFun.
~MyException
可以看出当抛出C++异常的copy语义,抛出异常后调用了Copy Constructor,用新建的异常对象传入catch中处理,所以在函数中改变了此异常对象后,再次抛出原异常,并不改变原有异常。这里我们经过一点小小的更改,看看会发生什么:
throwException
#include
#include
using namespace std;
class MyException : public exception
{
public:
MyException(const char* astrDesc)
{
mstrDesc = astrDesc;
}
MyException(const MyException& aoOrig)
{
cout <<"Copy Constructor MyException" < mstrDesc = aoOrig.mstrDesc;
}
MyException& operator=(const MyException& aoOrig)
{
cout <<"Copy Operator MyException" < if(&aoOrig == this)
{
return *this;
}
mstrDesc = aoOrig.mstrDesc;
return *this;
}
~MyException()
{
cout <<"~MyException" < }
string mstrDesc;
};
void exceptionFun()
{
try
{
throw MyException("A My Exception");
}
catch(MyException e)
{
cout < e.mstrDesc = "Changed exception.";
throw e;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
try
{
exceptionFun();
}
catch(MyException e)
{
cout < throw;
}
return 0;
}
这里和throwException程序的唯一区别就在于不是抛出原有异常,而是抛出改变后的异常,输出如下:
Copy Constructor MyException
A My Exception In exceptionFun.
Copy Constructor MyException
Copy Constructor MyException
~MyException
~MyException
Changed exception. Out exceptionFun.
~MyException
你会发现连续的两次Copy Constructor都是改变后的异常对象,这点很不可理解。。。。。。。因为事实上一次就够了。但是理解C++的Copy异常处理语义就好理解了,一次是用于传入下一次的catch语句中的,还有一次是留下来,当在外层catch再次throw时,已经抛出的是改变过的异常对象了,我用以下例子来验证这点:
throwTwiceException
#include
#include
using namespace std;
class MyException : public exception
{
public:
MyException(const char* astrDesc)
{
mstrDesc = astrDesc;
}
MyException(const MyException& aoOrig)
{
cout <<"Copy Constructor MyException" < mstrDesc = aoOrig.mstrDesc;
}
MyException& operator=(const MyException& aoOrig)
{
cout <<"Copy Operator MyException" < if(&aoOrig == this)
{
return *this;
}
mstrDesc = aoOrig.mstrDesc;
return *this;
}
~MyException()
{
cout <<"~MyException" < }
string mstrDesc;
};
void exceptionFun()
{
try
{
throw MyException("A My Exception");
}
catch(MyException e)
{
cout < e.mstrDesc = "Changed exception.";
throw e;
}
}
void exceptionFun2()
{
try
{
exceptionFun();
}
catch(MyException e)
{
cout < throw;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
try
{
exceptionFun2();
}
catch(MyException e)
{
cout < throw;
}
return 0;
}
输出如下,印证了我上面的说明。
Copy Constructor MyException
A My Exception In exceptionFun.
Copy Constructor MyException
Copy Constructor MyException
~MyException
~MyException
Changed exception. In exceptionFun2.
~MyException
Copy Constructor MyException
Changed exception. Out exceptionFuns.
上面像语言律师一样的讨论着C++本来已经足够简单的异常语法,其实简而言之,C++总是保持着一个上次抛出的异常用于用户再次抛出,并copy一份在catch中给用户使用。
但是,实际上,会发现,其实原有的异常对象是一直向上传递的,只要你不再次抛出其他异常,真正发生复制的地方在于你catch异常的时候,这样,当catch时使用引用方式,那么就可以避免这样的复制。
referenceCatch
#include
#include
using namespace std;
class MyException : public exception
{
public:
MyException(const char* astrDesc)
{
mstrDesc = astrDesc;
}
MyException(const MyException& aoOrig)
{
cout <<"Copy Constructor MyException: " < mstrDesc = aoOrig.mstrDesc;
}
MyException& operator=(const MyException& aoOrig)
{
cout <<"Copy Operator MyException:" < if(&aoOrig == this)
{
return *this;
}
mstrDesc = aoOrig.mstrDesc;
return *this;
}
~MyException()
{
cout <<"~MyException" < }
string mstrDesc;
};
void exceptionFun()
{
try
{
throw MyException("A My Exception");
}
catch(MyException& e)
{
cout < e.mstrDesc = "Changed exception.";
throw;
}
}
void exceptionFun2()
{
try
{
exceptionFun();
}
catch(MyException e)
{
cout < throw;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
try
{
exceptionFun2();
}
catch(MyException e)
{
cout < throw;
}
return 0;
}
上例中,使用引用方式来捕获异常,输出如下:
A My Exception In exceptionFun.
Copy Constructor MyException: Changed exception.
Changed exception. In exceptionFun2.
~MyException
Copy Constructor MyException: Changed exception.
Changed exception. Out exceptionFuns.
~MyException
完全符合C++的引用语义。
基本可以发现,做了很多无用功,因为try-catch无非是一层迷雾,其实这里复制和引用都还是遵循着原来的C++简单的复制,引用语义,仅仅这一层迷雾,让我们看不清楚原来的东西。所以,很容易理解一个地方throw一个对象,另外一个地方catch一个对象一定是同一个对象,其实不然,是否是原来那个对象在于你传递的方式,这就像这是个参数,通过catch函数传递进来一样,你用的是传值方式,自然是通过了复制,通过传址方式,自然是原有对象,仅此而已。
另外,最终总结一下,《C++ Coding Standards》73条建议Throw by value,catch by reference就是因为本文描述的C++的异常特性如此,所以才有此建议,并且,其补上了一句,重复提交异常的时候用throw;