13.14 处理 new 故障
处理 new 故障的方法有多种。到目前为止,我们介绍过用宏 assert 测试 new 返回的值。如果返回值为0,则assert宏终止程序。这不是处理new故障的健壮机制,它不允许我们用任何方法从故障恢复。ANSI/ISO C++ 草案标准指定,出现 new 故障时抛出bad_alloc异常(在头文件<new>中定义)。但许多编译器目前还不支持草案标准,仍然在new故障时返回0。本节介绍三个new故障的例子。第一个例子用Microsoft的Visual C++5.0编译,仍然在new故障时返回0。第二和第三个例子用Metrowerks Code Warrior Professional Releasel中的C++编译器编译,出现new故障时,它抛出bad_alloc异常。我们在不同的运行Windows95的计其机上测试三个程序,每台机器的内存和硬盘空间各不相同。
图 13.4 演示了 new 请求分配内存失败时返回0。第10行的for结构循环10次,每次循环分配5000000个double值的数组(即40000000字节,因为double通常为8个字节)。第13行的if结构测试每次new操作中是否顺利分配内存。如果new请求内存失败并返回0,则打印”Memory allocation failed\n”消息,循环终止。
1 // Fig. 13.4: fig13_04.cpp
2 // Demonstrating new returning 0
3 // when memory is not allocated
4 #include <iostream.h>
5
6 int main()
7 {
8 double *ptr[ 10 ];
9
10 for (int i = 0; i < 10; i++ ) {
11 ptr[ i] = new double[ 5000000 ];
12
13 if ( ptr[ i ] == 0 ) { // new failed to allocate memory
14 cout << "Memory allocation failed for ptr[ "
15 << i << "]\n";
16 break;
17 }
18 else
19 cout << "Allocated 5000000 doubles in ptr[ "
20 << i << "]\n";
21 }
22
23 return 0;
24 }
输出结果:
Allocated 5000000 doubles in ptr[ 0 ]
Allocated 5000000 doubles in ptr[ 1 ]
Memory allocation failed for ptr[ 2 ]
图 13.4 new 请求内存失败时返回 0
输出表示new失败和循环终止之前只进行了两欠循环。不同系统中的输出可能不同,取决于实际内存和可用的虚拟内存的磁盘空间。
图 13.5 演示了 new 请求内存失败时返回bad_alloc。第12行的for结构循环0次,每次循环分配5 000 000个double值的数组(即40 000 000字节,因为double通常为8个字节)。如果 new 失败并抛出 bad_alloc 异常,则终止循环,程序继续第18行的异常处理泫程捕获和处理异常,打印"Exception occurred"消息,然后exception what()返回异常特定消息的字符串(对于 bad_alloc 为 Alloccation Failure)。输出表示new失败和抛出bad_alloc异常之前只进行了三次循环。不同系统中的输出可能不同,取决于实际内存和可用的虚拟内存的磁盘空间。
1 // Fig. 13.5: fig13_05.cpp
2 // Demonstrating new throwing bad alloc
3 // when memory is not allocated
4 #include <iostream>
5 #include <new>
6
7 int main()
8 {
9 double *ptr[ 10 ];
10
11 try {
12 for( int i = 0; i < 10; i++ ) {
13 ptr[ i ] = new double[ 5000000 ];
14 cout << "Allocated 5000000 doubles in ptr[ "
15 << i << " ]\n";
16 }
17 }
18 catch ( bad_alloc exception ) {
19 cout << "Exception occurred:"
20 << exception.what() << endl;
21 }
22
23 return 0;
24 }
输出结果:
Allocated 5000000 doubles in ptr[ 0 ]
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处理器函数应完成下列任务:
- 通过删除其他动态分配内存以获得更多的内存空间,然后返回new运算符中的循环,试图再次分配内存。
- 抛出 bad_alloc 类型的异常。
- 调用函数 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