Fork me on GitHub

异常处理与命令空间

异常处理

异常处理(exception handling)机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并做出相应的处理。异常使得我们能够将问题的检测与解决过程分离开来。程序的一部分负责检测问题的出现,然后解决该问题的任务传递给程序的另一部分。检测环节无须知道问题处理模块的所有细节,反之亦然。

当执行一个throw时,跟在throw后面的语句将不再被执行。相反,程序的控制权从throw转移到与之匹配的catch模块。

因为跟在throw后面的语句将不再被执行,所以throw语句的用法有点类似于return语句:它通常作为条件语句的一部分或者作为某个函数的最后(或者唯一)一条语句。

栈展开

当抛出一个异常时,程序暂停当前函数的执行过程并立即开始寻找与异常匹配的catch字句。当throw出现在一个try语句块内时,检查与该try块关联的catch子句。如果找到了匹配的catch,就使用该catch处理异常。如果这一步没找到匹配的catch且该try语句嵌套在其他try块中,则继续检查与外层try匹配的catch子句。如果还是找不到匹配的catch,则退出当前的函数,在调用当前函数的外层函数中继续寻找,以此类推。

上述过程被称为栈展开(stack unwinding)过程。栈展开过程沿着嵌套函数的调用链不断查找,直到找到了与异常匹配的catch字句为止;或者也可能一直没找到匹配的catch,则退出主函数后查找过程终止。

假设找到了一个匹配的catch字句,则程序进入该子句并执行其中的代码。当执行完这个catch子句后,找到与try块关联的最后一个catch子句之后的点,并从这里继续执行。

如果没找到匹配的catch子句,程序将退出。因为异常通常被认为是妨碍程序正常执行的事件,所以一旦引发了某个异常,就不能对它置之不理。当找不到匹配的catch时,程序将调用标准库函数terminate,顾名思义,terminate负责终止程序的执行过程。

note: 一个异常如果没有被捕获,则它将终止当前的程序。

栈展开过程中对象被自动销毁

块退出后它的局部对象也将随之销毁,这条规则对于栈展开过程同样适用。如果在栈展开过程中退出了某个块,编译器将负责确保在这个块中创建的对象能被正确地销毁。

如果异常发生在构造函数中,则当前的对象可能只构造了一部分。即使某个对象只构造了一部分,我们也要确保已构造的成员能被正确地销毁。数组或标准库容器的元素初始化过程中,也是一样的。

析构函数与异常

在栈展开的过程中,运行类类型的局部对象的析构函数。因为这些析构函数是自动执行的,所以它们不应该抛出异常。一旦在栈展开的过程中析构函数抛出了异常,并且析构函数自身没能捕获到该异常,则程序将被终止。

异常对象

异常对象是一种特殊的对象,编译器使用异常抛出表达式来对异常对象进行拷贝初始化。

异常对象位于由编译器管理的空间中,编译器确保无论最终调用的是哪个catch子句都能访问该空间。当异常处理完毕后,异常对象被销毁。

当我们抛出一条表达式时,该表达式的静态编译时类型决定了异常对象的类型。

捕获异常

catch子句中的异常声明(exception declaration)看起来像是只包含一个形参的函数形参列表。像在形参列表中一样,如果catch无须访问抛出的表达式的话,则我们可以忽略捕获形参的名字。

通常情况下,如果catch接受的异常与某个继承体系有关,则最好将该catch的参数定义成引用类型。

查找匹配的处理代码

在搜寻catch语句的过程中,我们最终找到的catch未必是异常的最佳匹配。相反,挑选出来的应该是第一个与异常匹配的catch语句。因此,越是专门的catch越应该置于整个catch列表的前端。

因为catch语句是按照其出现的顺序逐一进行匹配的,所以当程序使用具有继承关系的多个异常时必须对catch语句的顺序进行组织和管理,使得派生类异常的处理代码出现在基类异常的处理代码之前。

命名空间

大型程序往往会使用多个独立开发的库,这些库又会定义大量的全局名字,如类、函数和模板等。当应用程序用到多个供应商提供的库时,不可避免地会在某些名字相互冲突的情况。多个库将名字放置在全局命名空间中将引发命名空间污染

命名空间(namespace)为防止名字冲突提供了更加可控的机制。命名空间分割了全局命名空间,其中每个命名空间是一个作用域。通过在某个命令空间中定义库的名字,库的作者(以及用户)可以避免全局名字固有的限制。

命名空间定义

一个命令空间的定义包含两个部分:首先是关键字namespace,随后是命名空间的名字。在命名空间名字后面是一系列由花括号括起来的声明和定义。只要能出现在全局作用域中的声明就能置于命名空间内,主要包括:类、变量(及其初始化操作)、函数(及其定义)、模板和其他命名空间。

using声明: 是一种将命名空间中的某个名字注入当前作用域的机制:

1
using std::cout;

上述语句使得命令空间std中的名字cout在当前作用域可见。之后,我们将可以直接使用cout而无须前缀std::了。

using指示: 是具有如下形式的声明:

1
using NS;

上述语句使得命名空间NS的所有名字在using指示所在的作用域以及NS所在的作用域都变得可见。

-------------本文结束感谢您的阅读-------------

本文地址:http://www.wangxinri.cn/2017/12/03/异常处理与命令空间/
转载请注明出处,谢谢!

梦想夹带眼泪,咸咸的汗水!