• C++ 基础教程
  • JavaScript入门教程
  • 博客文章
  • 访客留言
  • 联系站长
  • 5.3 出错处理 (Exception handling)

    本节介绍的出错处理是ANSI-C++ 标准引入的新功能。如果你使用的C++ 编译器不兼容这个标准,则你可能无法使用这些功能。

    在编程过程中,很多时候我们是无法确定一段代码是否总是能够正常工作的,或者因为程序访问了并不存在的资源,或者由于一些变量超出了预期的范围,等等。

    这些情况我们统称为出错(例外),C++ 新近引入的三种操作符能够帮助我们处理这些出错情况: try, throw 和 catch 。

    它们的一般用法是:

    	try {
    	    // code to be tried
    	    throw exception;
    } catch (type exception)
    {
    // code to be executed in case of exception }

    它们所进行的操作是:

    • try 语句块中的代码被正常执行。如果有例外发生,代码必须使用关键字throw 和一个参数来扔出一个例外。这个参数可以是任何有效的数据类型,它的类型反映了例外的特征。
    • 如果有例外发生,也就是说在try 语句块中有一个throw 指令被执行了,则catch 语句块会被执行,用来接收throw传来的例外参数。

    例如:

        // exceptions
        #include <iostream.h>
        
        int main () {
            char myarray[10];
            try {
                for (int n=0; n<=10; n++) {
                    if (n>9) throw "Out of range";
                    myarray[n]='z';
                }
            } catch (char * str) {
                cout << "Exception: " << str << endl;
            }
            return 0;
        }   
    		
    Exception: Out of range

    在这个例子中,如果在n 循环中,n 变的大于9 了,则仍出一个例外,因为数组 myarray[n] 在这种情况下会指向一个无效的内存地址。当throw 被执行的时候,try 语句块立即被停止执行,在try 语句块中生成的所有对象会被销毁。此后,控制权被传递给相应的catch 语句块(上面的例子中即执行仅有的一个catch)。最后程序紧跟着catch 语句块继续向下执行,在上面的例子中就是执行 return 0;.

    throw 语法与 return 相似,只是参数不需要扩在括号。

    catch 语句块必须紧跟着try 语句块后面,中间不能有其它的代码。catch 捕获的参数可以是任何有效的数据类型。catch 甚至可以被重载以便能够接受不同类型的参数。在这种情况下被执行catch 语句块是相应的符合被throw扔出的参数类型的那一个:

        // exceptions: multiple catch blocks
        #include <iostream.h>
        
        int main () {
            try {
                char * mystring;
                mystring = new char [10];
                if (mystring == NULL) throw "Allocation failure";
                for (int n=0; n<=100; n++) {
                    if (n>9) throw n;
                    mystring[n]='z';
                }
            } catch (int i) {
                cout << "Exception: ";
                cout << "index " << i << " is out of range" << endl;
            } catch (char * str) {
                cout << "Exception: " << str << endl;
            }
            return 0;
        }    
    			
    Exception: index 10 is out of range

    在上面的例子中,有两种不同的例外可能发生:

    1. 如果要求的10个字符空间不能够被赋值(这种情况很少,但是有可能发生):这种情况下扔出的例外会被catch (char * str)捕获。
    2. n超过了mystring的最大索引值(index):这种情况下扔出的例外将被catch (int i)捕获,因为它的参数是一个整型值。

    我们还可以定义一个catch 语句块来捕获所有的例外,不论扔出的是什么类型的参数。这种情况我们需要在catch 或面的括号中写三个点来代替原来的参数类型和名称,如:

       try {
    // code here } catch (...) { cout << "Exception occurred"; }

    try-catch 也是可以被嵌套使用的。在这种情况下,我们可以使用表达式throw;(不带参数)将里面的catch 语句块捕获的例外传递到外面一层,例如:

       try {
    try {
    // code here } catch (int n) { throw; } } catch (...) { cout << "Exception occurred"; }

    没有捕获的例外 (Exceptions not caught)

    如果由于没有对应的类型,一个例外没有被任何catch 语句捕获,特殊函数terminate 将被调用。

    这个函数通常已被定义了,以便立即结束当前的进程(process),并显示一个“非正常结束”( "Abnormal termination")的出错信息。它的格式是:

    void terminate();

    标准例外 (Standard exceptions)

    一些C++ 标准语言库中的函数也会扔出一些列外,我们可以用try 语句来捕获它们。这些例外扔出的参数都是std::exception 引申出的子类类型的。这个类(std::exception) 被定义在C++ 标准头文件 中,用来作为exceptions标准结构的模型:

    因为这是一个类结构,如果你包括了一个catch 语句块使用地址(reference)来捕获这个结构中的任意一种列外 (也就是说在类型后面加地址符 &),你同时可以捕获所有引申类的例外 (C++的继承原则)。

    下面的例子中,一个类型为 bad_typeid 的例外(exception的引申类),在要求类型信息的对象为一个空指针的时候被捕获:

        // standard exceptions
        #include <iostream.h>
        #include <exception>
        #include <typeinfo>
        
        class A {virtual void f() {}; };
        
        int main () {
            try {
                A * a = NULL;
                typeid (*a);
            } catch (std::exception& e) {
                cout << "Exception: " << e.what();
            }
            return 0;
        }			
    Exception: Attempted typeid of NULL pointer

    你可以用这个标准的例外层次结构来定义的你的例外或从它们引申出新的例外类型。