动态内存的结果问题: 动态内存申请一定成功吗?常见的动态内存分配代码C 代码:void code(){ int* p = (int*)malloc(10 * sizeof(int)); if( p != NULL ) { // … } free(p);}C++ 代码:void code(){ int* p = new int[10]; if( p != NULL ) { // … } delete p;}必须知道的事实malloc 函数申请失败时返回 NULL 值new 关键字申请失败时(根据编译器不同)返回 NULL 值 (古代)抛出 std::bad_alloc 异常 (现代)问题: new 语句中的异常是怎么抛出来的呢?new 关键字在 C++ 规范中的标准行为在堆空间申请足够的内存成功:在获取的空间中调用构造函数创建对象返回对象的地址失败抛出 std::bad_alloc 异常new 关键字在 C++ 规范中的标准行为new 在分配内存时如果空间不足,会调用全局的 new_handler() 函数new_handler() 函数中抛出 std::bad_alloc 异常可以自定义 new_handler() 函数处理默认的 new 内存分配失败的情况new_handler() 中,可以手动做一些内存整理的工作,使得更多的堆空间可以被使用。new_handler() 函数的替换自定义一个无返回值无参数的函数调用 set_new_handler() 设置自定义的函数参数类型为 void()()返回值为默认的 new_handler() 函数入口地址new_handler() 的定义和使用void my_new_handler(){ cout << “No enough memory” << endl;}int main(int argc, char argv[]){ set_new_handler(my_new_handler); // … return 0;}编程实验: new_handler 初探公用实验代码:#include <iostream>#include <new>#include <cstdlib>#include <exception>using namespace std;class Test{private: int m_value; public: Test() { cout << “Test()” << endl; m_value = 0; } ~Test() { cout << “~Test()” << endl; } void* operator new (unsigned int size) { cout << “operator new: " << size << endl; // return malloc(size); return NULL; // 注意这里! 模拟内存申请失败 } void operator delete(void* p) { cout << “operator delete: " << p << endl; free(p); } void* operator new[] (unsigned int size) { cout << “operator new[]: " << size << endl; // return malloc(size); return NULL; // 注意这里! 模拟内存申请失败 } void operator delete[](void* p) { cout << “operator delete[]: " << p << endl; free(p); }};实验 1:不同编译器中 new_handler() 行为void my_new_handler(){ cout << “void my_new_handler()” << endl;}void ex_func_1(){ new_handler func = set_new_handler(my_new_handler); // 注意这里! try { cout << “func = " << func << endl; if( func ) { func(); } } catch(const bad_alloc&) { cout << “catch(catch bad_alloc&)” << endl; }}int main(){ ex_func_1(); return 0;}输出:[g++]func = 0输出:[vc++2010]func = 00000000输出:[bcc]func = 0x00401474catch(catch bad_alloc&)结论:默认情况下,g++,vc++2010 并没有提供全局的 new_handler() 函数; gcc 提供了全局的 new_handler() 函数,并抛出了 bad_alloc 异常。 实验 2:不同编译器 new 失败行为void ex_func_2(){ Test* pt = new Test(); cout << “pt = " << pt << endl; delete pt; pt = new Test[5]; cout << “pt = " << pt << endl; delete[] pt;}int main(){ ex_func_2(); return 0;}输出:[g++]Test()段错误输出:[vc++2010]operator new: 4pt = 00000000operator new[]: 24pt = 00000000输出:[bcc]operator new: 4pt = 00000000operator new[]: 24pt = 00000000分析:g++ 编译生成的可执行文件运行时发生段错误: new 重载函数返回 NULL, 于是就在 0 地址处创建对象,调用构造函数。构造函数中,m_value = 0,导致段错误。vc++2010:如果 new 的重载返回 NULL,对象未创建,构造函数不会被调用bcc:如果 new 的重载返回 NULL,对象未创建,构造函数不会被调用问题:如何跨编译器统一 new 的行为, 提高代码的移植性呢?解决方案【动态内存分配失败时,返回空指针】全局范围(不推荐)重新定义 new / delete 的实现,不抛出任何异常自定义 new_handler() 函数,不抛出任何异常类层次范围重载 new / delete, 不抛出任何异常单词动态内存分配使用 nothrow 参数,指明 new 不抛出异常编程实验:动态内存申请实验 1: 类层次范围#include <iostream>#include <new>#include <cstdlib>#include <exception>using namespace std;class Test{private: int m_value; public: Test() { cout << “Test()” << endl; m_value = 0; } ~Test() { cout << “~Test()” << endl; } void* operator new (unsigned int size) throw() // 注意这里! { cout << “operator new: " << size << endl; // return malloc(size); return NULL; // 注意这里! 模拟内存申请失败 } void operator delete(void* p) { cout << “operator delete: " << p << endl; free(p); } void* operator new[] (unsigned int size) throw() // 注意这里! { cout << “operator new[]: " << size << endl; // return malloc(size); return NULL; // 注意这里! 模拟内存申请失败 } void operator delete[](void* p) { cout << “operator delete[]: " << p << endl; free(p); }};void ex_func_2(){ Test* pt = new Test(); cout << “pt = " << pt << endl; delete pt; pt = new Test[5]; cout << “pt = " << pt << endl; delete[] pt;}int main(){ ex_func_2(); return 0;}输出:[g++]operator new: 4pt = 0operator new[]: 24pt = 0输出:[vc++2010]operator new: 4pt = 00000000operator new[]: 24pt = 00000000输出:[bcc]operator new: 4pt = 00000000operator new[]: 24pt = 00000000实验 2:单次动态内存分配范围#include <iostream>#include <new>#include <cstdlib>#include <exception>using namespace std;void ex_func_3(){ int* p = new(nothrow) int[10]; // 注意这里! cout << “p = " << p << endl; delete p;}int main(){ ex_func_3(); return 0;}输出:p = 0x8300008实验结论不是所有的编译器都遵循 C++ 的标准规范编译器可能重定义 new 的实现,并在实现中抛出 bad_alloc 异常编译器的默认实现中,可能没有设置全局的 new_handler() 函数对于移植性要求较高的代码,需要考虑 new 的具体细节小结不同的编译器在动态内存分配上的实现细节不同malloc 函数在内存申请失败时返回 NULL 值new 关键字在内存申请失败时可能返回 NULL 值可能抛出 bad_alloc 异常关于 new 的小知识点补充:在指定的内存空间上创建对象(需要手动调用析构函数)#include <iostream>using namespace std;int main(){ int bb[2] = {0}; struct ST { int x; int y; }; ST* pt = new(bb) ST(); // 注意这里! pt->x = 1; pt->y = 2; cout << bb[0] << endl; cout << bb[1] << endl; pt->~ST(); // 手动调用析构函数 return 0;}输出:12以上内容参考狄泰软件学院系列课程,请大家保护原创!