外传篇3 动态内存申请的结果
1.动态内存申请一定成功吗?
(1)问题:动态内存申请一定成功吗?
(2)常见的动态内存分配代码:
C代码:
C++代码:
(3)必须知道的事实!
-
malloc函数申请失败时返回NULL值
-
new关键字申请失败时(根据编译器的不同)
-
返回NULL值
-
抛出 std::bad_alloc 异常
-
(4)问题:new语句中的异常是怎么抛出来的?
—new关键字在C++规范中的标准行为:
-
在堆空间申请足够大的内存
-
成功:
- 在获取的空间中调用构造函数创建对象
- 返回对象的地址
-
失败:
-
抛出 std::bad_alloc 异常
-
-
-
new在分配内存时
-
如果空间不足,会调用全局的 new_handler() 函数
-
new_handler() 函数中抛出 std::bad_alloc 异常
-
-
可以自定义 new_handler() 函数
- 处理默认的new内存分配失败的情况
2.new_handler() 函数
(1)new_handler() 的定义和使用:
(2)问题:如何跨编译器统一new 的行为,提高代码移植性?
(3)解决方案:
-
全局范围(不推荐)
-
重新定义 new / delete 的实现,不抛出任何异常
-
自定义 new_handler() 函数,不抛出任何异常
-
-
类层次范围
- 重载 new / delete,不抛出任何异常
-
单次动态内存分配
-
使用 nothrow 参数,指明 new 不抛出异常
-
编程实验1:证明存在 new_handler() 函数
#include <iostream> #include <cstdlib> using namespace std; class Test { int m_value; public: Test() { cout << "Test()" << endl; m_value = 0; } ~Test() { cout << "~Test()" << endl; } void* operator new (unsigned long 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 long size) { cout << "operator new[]: " << size << endl; return malloc(size); } void operator delete[] (void* p) { cout << "operator delete[]: " << p << endl; free(p); } }; 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(const bad_alloc&)" << endl; } } int main(int argc, char *argv[]) { ex_func_1(); return 0; }
运行结果:
[root@bogon Desktop]# g++ test.cpp
test.cpp: In static member function ‘static void* Test::operator new(long unsigned int)’:
test.cpp:28: warning: ‘operator new’ must not return NULL unless it is declared ‘throw()’ (or -fcheck-new is in effect)
[root@bogon Desktop]# ./a.out
func = 0
(在g++编译器中没有设置这个全局的 new_handler() 函数,bcc编译器中实现了这个全局的 new_handler() 函数)
编程实验2:动态内存申请失败的结果
#include <iostream> #include <cstdlib> using namespace std; class Test { int m_value; public: Test() { cout << "Test()" << endl; m_value = 0; } ~Test() { cout << "~Test()" << endl; } void* operator new (unsigned long 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 long size) { cout << "operator new[]: " << size << endl; return malloc(size); } 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; } int main(int argc, char *argv[]) { ex_func_2(); return 0; }
运行结果:
[root@bogon Desktop]# g++ test.cpp
test.cpp: In static member function ‘static void* Test::operator new(long unsigned int)’:
test.cpp:28: warning: ‘operator new’ must not return NULL unless it is declared ‘throw()’ (or -fcheck-new is in effect)
[root@bogon Desktop]# ./a.out
operator new: 4
Test()
Segmentation fault (core dumped)
(g++编译器中报错:段错误。不同的编译器报错信息不同)
编程实验3:统一不同编译器动态内存申请失败后的行为
#include <iostream> #include <cstdlib> using namespace std; class Test { int m_value; public: Test() { cout << "Test()" << endl; m_value = 0; } ~Test() { cout << "~Test()" << endl; } void* operator new (unsigned long 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 long 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(int argc, char *argv[]) { ex_func_2(); return 0; }
运行结果:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
operator new: 4
pt = 0
operator new[]: 28
pt = 0
(显示的调用析构函数)
编程实验4:让编译器申请失败后返回空指针而不是抛出异常
#include <iostream> #include <cstdlib> using namespace std; class Test { int m_value; public: Test() { cout << "Test()" << endl; m_value = 0; } ~Test() { cout << "~Test()" << endl; } void* operator new (unsigned long 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 long 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_3() { int* p = new(nothrow) int[10]; // ... ... delete[] p; int bb[2] = {0}; struct ST { int x; int y; }; ST* pt = new(bb) ST(); // new():在指定空间上创建对象 pt->x = 1; pt->y = 2; cout << bb[0] << endl; cout << bb[1] << endl; pt->~ST(); // 显示的调用析构函数 } int main(int argc, char *argv[]) { ex_func_3(); return 0; }
运行结果:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
1
2
编程实验:VS2010 new实现
/*** *new.cxx - defines C++ new routine * * Copyright (c) Microsoft Corporation. All rights reserved. * *Purpose: * Defines C++ new routine. * *******************************************************************************/ #ifdef _SYSCRT #include <cruntime.h> #include <crtdbg.h> #include <malloc.h> #include <new.h> #include <stdlib.h> #include <winheap.h> #include <rtcsup.h> #include <internal.h> void * operator new( size_t cb ) { void *res; for (;;) { // allocate memory block res = _heap_alloc(cb); // if successful allocation, return pointer to memory if (res) break; // call installed new handler if (!_callnewh(cb)) break; // new handler was successful -- try to allocate again } RTCCALLBACK(_RTC_Allocate_hook, (res, cb, 0)); return res; } #else /* _SYSCRT */ #include <cstdlib> #include <new> _C_LIB_DECL int __cdecl _callnewh(size_t size) _THROW1(_STD bad_alloc); _END_C_LIB_DECL void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) if (_callnewh(size) == 0) { // report no memory static const std::bad_alloc nomem; _RAISE(nomem); } return (p); } /* * Copyright (c) 1992-2002 by P.J. Plauger. ALL RIGHTS RESERVED. * Consult your license regarding permissions and restrictions. V3.13:0009 */ #endif /* _SYSCRT */
实验结论:
-
不是所有的编译器都遵循C++的标准规范
-
编译器可能重定义 new 的实现,并在实现中抛出 bad_alloc 异常
-
编译器的默认实现中,可能没有设置全局的 new_handler() 函数
-
对于移植性要求较高的代码,需要考虑 new 的具体细节
3.小结
-
不同的编译器在动态内存分配上的实现细节不同
-
malloc 函数在内存申请失败时返回NULL值
-
new 关键字在内存申请失败时
-
可能返回NULL值
-
可能抛出 bad_alloc 异常
-