C++异常处理模型除了支持面向过程的C风格程序中的异常处理外(就是没有面向对象的概念,完全是C程序,整个程序实际就是函数的集合,但却用C++编译器来编译这样的C程序,所以这样的程序中是可以a使用C++的异常处理机制的,要不怎么说C++是兼容C语言的呢?但是需要注意的是,单纯的C语言程序中是不能使用C++异常处理模型进行编程的。是不是有点说拗口了?有点糊涂了呢?其实很简单,那就是如果程序中使用了C++异常处理机制,也即代码中有try、catch和throw关键字,那么就必须使用C++编译器来编译这个程序。许多程序员朋友们在这里有一个理解上的误区,认为只有程序中使用了面向对象的概念,即使用class关键字来定义一个类结构才算得上C++程序,其实这种理解是片面的,如果程序中采用了C++异常处理机制,那么也有理由认为这是一个C++程序,哪怕程序的代码完全是C语言风格的,并且这样的程序用C编译器来编译肯定将会报错,提示未定义的try标示符等等错误信息),还支持面向对象程序中对象抛出的异常处理。
C++异常处理模型的确和面向对象是紧密结合的,除了在相遇篇中介绍到的用对象来描述程序中出现的异常之外,C++异常处理模型也对在面向对象程序中的对象实例所抛出的异常作了最完善的支持和处理。也许大家会觉得这很容易,没什么了不起的地方。但恰恰相反,实际上这才是C++异常处理模型最成功、最不可思议和最闪光的地方。而且由于C++异常处理模型对面向对象有了很好的支持和兼容,才使得C++异常处理模型本身的实现变得特别复杂,因为它需要跟踪每一个对象的运行情况和状态(关于C++异常处理模型的实现,会在爱的秘密篇中有详细剖析)。本文和接下来的几篇文章将讲述当对象实例抛出异常时将如何处理。
对象的生命周期一般有三种状态:构造、运行和析构销毁。因此对象抛出的异常也有这三种区别。是在对象构造时抛出的呢?还是对象运行时抛出的呢?或是析构对象时抛出的?这三种不同时候抛出的异常会将会产生不同的结果。本文首先讨论最常见的一种情况,在对象运行时抛出的异常,也即执行对象的成员函数时出现的异常。
对象的成员函数抛出的异常
1、老方法,看例子先,如下:
class MyTest_Base
{
public:
MyTest_Base (string name = “”) : m_name(name)
{
cout << “构造一个MyTest_Base类型的对象,对象名为:”<<m_name << endl;
}
virtual ~ MyTest_Base ()
{
cout << “销毁一个MyTest_Base类型的对象,对象名为:”<<m_name << endl;
}
void Func() throw()
{
throw std::exception(“故意抛出一个异常,测试!”);
}
void Other() {}
protected:
string m_name;
};
void main()
{
try
{
MyTest_Base obj1(“obj1”);
// 调用这个成员函数将抛出一个异常,注意obj1的析构函数会被执行吗?如果
// 会,又是在什么时候被执行呢?
obj1.Func();
obj1.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << “unknow exception”<< endl;
}
}
C++程序员不难看出上面的程序的运行结果,如下:
构造一个MyTest_Base类型的对象,对象名为:obj1
销毁一个MyTest_Base类型的对象,对象名为:obj1
故意抛出一个异常,测试!
从运行结果可以得出如下结论:
(1) 对象的成员函数出现异常时,catch block能捕获到异常,这一点就像C语言中的普通函数一样,没什么特别的地方;
(2) 对象的成员函数出现异常时,对象的析构函数将会得到执行(这一点很神奇吧!当然在这里不会做过多研究,在剖析C++异常处理模型的实现时再做详细的阐述),这里与C++标准中规定的面向对象的特性是相一致的,构造了的对象就必须保证在适当的地方要析构它,以释放可能的资源。因此前面说的“C++异常处理模型对面向对象提供了支持和兼容”是有根据的。而且注意它的析构函数是在异常处理模块之前执行的,这一点更与C++标准中规定的面向对象的特性是一致的,当对象出了作用域时,它就必须要被析构。
2、把上面的程序小改一下,运行再看结果,如下:
void main()
{
// obj1对象不在trycatch域中,注意它的析构函数在什么时候被执行?
MyTest_Base obj1(“obj1”);
try
{
// obj2和obj3对象都在trycatch域中,其中obj3.Func()函数被调用,因此
// obj3会抛出异常,特别需要注意的是,obj2的析构函数会被执行吗?如果
// 会,又是在什么时候被执行呢?
MyTest_Base obj2(“obj2”), obj3(“obj3”);
obj3.Other();
// 调用这个成员函数将抛出一个异常
obj3.Func();
// 注意:obj4对象在构造之前,函数中就有异常抛出。所以obj4对象将不会
// 被构造,当然也不会被析构
MyTest_Base obj4(“obj4”);
obj3.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << “unknow exception”<< endl;
}
}
上面的程序也难看出其运行结果,如下:
构造一个MyTest_Base类型的对象,对象名为:obj1
构造一个MyTest_Base类型的对象,对象名为:obj2
构造一个MyTest_Base类型的对象,对象名为:obj3
销毁一个MyTest_Base类型的对象,对象名为:obj3
销毁一个MyTest_Base类型的对象,对象名为:obj2
故意抛出一个异常,测试!
销毁一个MyTest_Base类型的对象,对象名为:obj1
结合程序中提出的问题和运行结果,可以又可得出如下结论:
(1) 在成员函数出现异常时,同一个作用域中异常出现点后面还未来得及构造的对象将不会被构造,当然也不会被析构;
(2) 在成员函数出现异常时,同一个作用域中异常出现点前面已经构造的对象也同样会被析构(这是不是更神奇了!)。因此这也显现出C++异常处理不会破坏C++标准中规定的面向对象的特性,当对象出了作用域时,它就必须要被析构,即便它自己本身没出现异常,总之不管是正常的执行过程导致对象退出了作用域,还是其它对象运行时发生了异常而导致自己退出了作用域;
(3) 在成员函数出现异常时,未被影响到的其它作用域中的对象将保持自己原来的执行流程。
对象的成员函数抛出的异常时概括性总结
哈哈^-^,其是就只有一句话,那就是“C++的异常处理不会破坏任何一条面向对象的特性!”,因此主人公阿愚建议大家其实无须要记住上面总结的n条结论,记住这一条足矣!
下篇文章讨论在构造函数中抛出异常时程序的执行情况,这有点复杂呀!朋友们,Let's go!
评论