⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 13章 异常处理.txt

📁 C++大学教程txt版中文版 C++大学教程txt版中文版
💻 TXT
📖 第 1 页 / 共 5 页
字号:
Allocated 5000000 doubles in ptr[ 1 ]
Allocated 5000000 doubles in ptr[ 2 ]
Exception occurred: Allocation Failure

                              图 13.5 new请求内存失败时返回 bad_alloc

    编译器对new故障处理的方法各不相同。一般情况下,许多当前的和原来的C++编译器在new失败时默认返回0。有些编译器在包括头文件<new><或<new.h,)时支持抛出异常。有些编译器(如Code Warrior Professional Release 1)不管是否包括头文件<new>默认抛出bad_alloc。详见编译器文档中关于编译器对new故障处理方法的说明。
    ANSI/SO C++草案标准指定标准支持的编译器在new失败时仍然可以用返回0的版本。为此,头文件<new>定义类型nothrow,使用如下:
    double‘*ptr = new(nothrow)double[5000000];
  上述语句表示用不支持抛出bad_alloc异常的new版本(即nothrow)分配5000000个double值的数组。

    软件工程视点13. 11
    为了使程序更健壮,ANSI/ISO c++草案标准建议程序员应使用抛出bad_alloc异常的new版本。

    还可以用其他特性进行new故障处理。函数set_new_handler(原型在头文件<new>或<new.h>中)取一个函数指针作为参数,所指函数不取参数并返回void。函数指针注册为new失败时要调用的函数。这样就向程序员提供了处理每个new故障的一致方法,而不管故障发生在程序中哪个地方。
    程序中用set_new_handler注册new处理器之后,new不会在故障时抛出bad_alloc。
    new运算符实际上是一个循环,请求所要的内存。如果内存分配成功,则new返回该内存的指针。如果内存分配失败,且没有用set_new_handler注册new处理器函数,则new抛出bad_alloc异常。如果new无法分配内存而注册了new处理器函数,则调用new处理器函数。C++草案标准指定new处理器函数应完成下列任务:
    1.通过删除其他动态分配内存以获得更多的内存空间,然后返回new运算符中的循环,试图再次分配内存。
    2.抛出bad_alloc类型的异常。
    3.调用函数abort或exit(都在<csdtlib>或<stdlib.h,头文件中定义)终止程序。
    图13.6的程序演示set_new_handler。函数customNewHandler只是打印错误消息和调用abort终止程序。输出显示new失败和抛出bad_alloc异常之前循环进行了三次迭代。不同系统中的输出可能不同,取决于实际内存和可用的虚拟内存的磁盘空间。
1 // rig.13.6:fig13_06.cpp
2 // Demonstrating set_new_handler
3 #include<iostream.h>
4 #include<new.h>
5 #include<stdlib.h>
6
7 void customNewHandler()
8 {
9   cerr << "customNewHandler was called";
10   abort();
11 }
12
15 int main()
14 {
15   double * ptr[ 10 ];
16   setnewhandler(customNewHandler);
17
18   for ( int i = 0; i < 10; i++ ) {
19     ptr[ i ]   new double[ 5000000 ];
2O
21         cout << "Allocated 5000000 doubles in ptr[ "
22         << i << "]\n";
23   }
24
25   return 0;
26 }

输出结果:
Allocated 5000000 doubles in ptr[ 0 ]
Allocated 5000000 doubles in ptr[ 1 ]
Allocated 5000000 doubles in ptr[ 2 ]
CustomNewHandler was called

                                图 13.6 演示set_new_handler

 
C++大学教程 第13章 异常处理  
 
13.15 auto_ptr类与动态内存分配
    一个常见的编程习惯是在空闲存储区中动态分配内存(可能是对象),将该内存的地址赋给一个指针,使用这个指针操作内存,并在该内存不再需要时用delete释放内存。如果内存分配之后和执行delete语句之前发生异常,则可能发生内存泄漏。ANSI/ISO C++草案标准提供<memory>头文件中的auto_ptr类模板,可以解决这个问题。
    auto_ptr类对象维护动态分配内存的指针。当auto_ptr对象超出范围时,对指针数据成员进行一个delete操作。auto_ptr类模板提供了+和->运算符.因此auto_ptr对象可以像普通指针变量一样使用。图13.7演示auto_ptr对象指向Integer类对象(在第8行到第18行定义)。
I // Fig. 13.7: fig13_07.cpp
2 // Demonstrating auto_ptr
3 #include <iostream>
4 #include <memory>
5
6 using namespace std;
7
8 class Integer {
9 public:
10   Integer( int i = 0 ) : value( i )
11     {cout << "Constructor for Integer " << value << endl; }
12   ~Integer()
13     {cout << "Destructor for Integer " << value << endl; }
14   void setInteger( int i ) { value = i; }
15   int getInteger() const { return value; }
16 private:
17   int value;
18 };
19
20 int main()
21 {
22   cout << "Creating an auto_ptr object that points "
23       << "to an Integer\n";
24
25   auto_ptr< Integer > ptrToInteger( new Integer( 7 ) );
26
27   cout << "using the auto_ptr to manipulate the Integer\n";
28   ptrToInteger->setInteger( 99 );
29   cout << "Integer after setInteger: "
30       << ( *ptrToInteger ).getInteger()
31       << "\nTerminating program" << endl;
32
33   return 0;
34 }

输出结果:

Creating an auto_ptr object that points to an Integer
Constructor for Integer 7
Using the auto_ptr to manipulate the Integer
Integer after setInteger: 99
Terminating program
Destruct for Integer 99

                               图 13.7演示auto_ptr

    第25行:
  auto_ptr<Integer> ptrToInteger(new Integer(7 ));
生成aut_ptr对象ptrToInteger并用动态分配Inleger对象的指针(包含数值7)初始化。
    第28行:
  ptrToInteger->setInteger(99);
用auto_ptr重载->运算符和函数调用运算符()调用ptrToInteger所指Integer对象的setInteger函数。
    第30行:
    (*ptrToInteger).getInteger()
用auto_ptr重载*运算符复引用ptrToInteger,然后用圆点运算符和函数调用运算符()调用ptrTolmeger所指Integer对象的getInteger函数。
    由于ptrToInteger是main中的局部自动变量,因此main终止时删除ptrToInteger。这样就强制删除ptrToInteger所指的Integer对象,从而强制调用Integer类的析构函数。更重要的是这个方法可以防止内存泄漏。

13.16 标准库异常层次
    经验表明,异常是可以分类的。C++草案标准提供了标准库异常层次。这个层次以基类excephon开始(在头文件<exception>中定义),该基类提供服务what(),在每个派生类中重定义,发出相应的错误消息。
    从基类exception可以派生直接派生类runtime_error和Iogic_error(都在头文件<stdexcept>中定义),每个派生类又可以派生其他类。
    从exception中还可以派生由于C++语言特性而抛出的异常,例如,new抛出bad_alloc(13.14节).dynamic_cast抛出bad_cast(第2l章),typeid抛出bad_typeid(第21章)。如果发生意外异常时,通过在函数的抛出表中加上std::bad_exeeption,unexpected()抛出bad_exception而不是(默认)终止程序或调用set_unexpected指定的另一函数。
    logic_error类是几个标准异常类的基类,表示程序逻辑中的错误,可以通过编写正确的代码来防止。下面介绍其中的一些类。invalid_argument类表示向函数传入无效参数(可以通过编写正确的代码来防止)。length_error类表示长度大于所操作对象允许的最大长度(第19章处理string时会抛出length_error异常)。out_of_range类表示数组和string下标之粪的值超界。
    runtime_error类是几个其他异常类的基类,表示程序中只能在执行时发现的错误。overflow_error类表示发生运算上溢错误;underflow_error类表示发生运算下溢错误。

    软件工程视点13.12
    标准exception层次只是一个起点.用户可“抛出标准异常、抛出从标准异常派生的异常或抛出不是从标准异常派生的异常。
    常见编程错误13.14
    用户自定义异常类不一定从exception类派生。  因此catch(exception并井不保证捕获程序可能遇到的各种异常。
    测试与调试提示13. 3
    要捕获try块可能抛出的所有异常,用catch(...)。


小  结

    ●异常的常见例子有new无法取得所需内存、数组下标超界、运算溢出、除数为0和无效函数参数。
    ●异常处理使程序可以捕获和处理错误,而不是任其发生和造成恶果。如果程序员不提供处理致命错误的措施,则程序终止。非致命错误通常允许程序继续执行,但会产生错误结果。
    ●异常处理可以处理同步错误作为程序执行的结果。
    ●异常处理并不处理异步情况,如磁盘I/O完成、网络消息到达、鼠标单击等等,这些情况最好用其他方法处理,如中断处理。
    ●异常处理通常用于发现错误的部分与处理错误的部分在不同部分(不同范围)的情况。
    ●异常不应为具体的控制流作为一种变换机制使用。控制流和常用的控制结构一般都比异常更清晰、更高效。
    ●异常处理应当用于处理程序组件中与这些异常处理没有直接关系的异常。
    ●异常处理应当用于处理函数、库、类等常用软件组件中的异常和组件本身不处理异常的情况。
    ●异常处理应当用于在大型项目中以统一方式处理整个项目异常。
    ●C++异常处理用于错误检测函数无法处理错误的情况,这种函数抛出异常。如果异常与catch块中的参数类型相符,则执行该catch块的代码。如果找不到相应的异常处理器,则调用termianate函数(默认调用函数abort)。
    ●程序员在try块中放上出错时产生异常的代码。try块后面是一个或几个catch块。每个catch块指定捕获和处理一种异常。每个catch块包含一个异常处理器。
    ●抛出异常时,程序控制离开时块,从catch块中搜索相应的异常处理器。如果try块中没有抛出异常,则跳过该块的异常处理器,程序在最后一个catch块之后恢复执行。
    ●函数的try块中抛出异常,或者从try块直接或间接调用的函数抛出异常。
    ●抛出异常之后,控制无法返回抛出点。
    ●发生异常时,可以从异常点向异常处理器传递信息。这些信息是抛出对象的类型或抛出对象中的信息。
    ●常见异常类型是char*,该类型只是包括一个错误消息,作为throw的操作数。
    ●异常指定可以由指定函数抛出一列异常,将空异常指定语句放在函数的参数表之后表示该函数不抛出任何异常。
    ●抛出异常时,指定相应类型的最近一个异常处理器(对抛出该异常的try块)捕获这个异常。
    ●抛出异常时,生成和初始化throw操作数的一个临时副本,然后这个临时对象初始化异常处理器中的参数。异常处理器执行完毕和退出时,删除临时对象。
    ●不一定总是显式检查错误。try块可能不包含错误检查和throw语句,但时块中所指的代码可能导致执行构造函数中的错误检查代码。
    ●异常会终止异常所在的程序块。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -