内存分配不一定成功
常见内存分配的方法
code about c
int* p = (int*)malloc(10 * sizeof(int));
if( p != NULL )
{
// ...
...
}
这种写法是合法的
code about c++
int* p = new int[10];
if( p != NULL )
{
// ...
...
}
对于古代编译器而言这种写法是合法的。对于现代编译就不合法了
- 申请成功i的时候这句话没有意义
- 申请失败的时候编译器会抛出异常
对于现代编译器而言:无论成功与失败都没有必要用 if()语句判断
申请内存失败时:
1,malloc 函数申请失败时返回 NULL 值;
2,new 关键字申请失败时(根据编译器的不同):
- 返回 NULL 值;古代编译器兼容 C 中方式,返回 NULL 值;
-
抛出 std::bad_alloc 异常;近代编译器不会返回 NULL 值,而是抛出一个标准库中的 std::bad_alloc 异常;
problem: new 语句中的异常是怎么抛出来的?
new 关键字在 C++ 规范中的标准行为:在堆空间申请足够大的内存;
成功:
- 在获取的空间中调用构造函数创建对象; 返回对象的地址;
失败:
C++ 规范定义要抛出 std::bad_alloc 异常;
new 在分配空间时:
- 如果空间不足,会调用全局的 new_handler() 函数 。调用 new_handler() 函数的意义在于我们可以有机会做一些处理,使得有更多的空间可以空出来;整理空间、空出足够的内存 C++ 编译器是不知道的,这件事情具体的平台具体讨论,于是 C++ 标准规范就给了默认的 new_handler() 实现,即抛出异常;
- new_handler() 函数中抛出 std::bad_alloc 异常;
- 可以自定义 new_handler() 函数:处理默认的 new 内存分配失败的情况;C++ 平台不能整理空间、空出足够的内存,那么就交给我们自己来定义;
void my_new_handler()
{
cout << "No enough memory";
cout << endl;
exit(1); // 内存不足了,就把当前的程序结束了;
}
int main(int argc, char* argv[])
{
set_new_handler(my_new_handler); // 告诉 C++ 编译器;可以设置自定义的处理函数,处理堆空间不足的情况;
// ... ...
return 0;
}
针对不同的编译器
如果跨编译器统一 new 的行为,提高代码移植性?
无论在任何情况下,申请失败都返回空或者抛出异常;
为了兼顾古代编译器,一般做法是自定义 new,使得 new 在申请堆空间失败的时候,直接返回空指针,而不抛出异常;
### 解决方案
全局范围(不推荐):
1,重新定义 nwe/delete 的实现,不抛出任何异常;
2,自定义 new_handler() 函数,不抛出任何异常;
- 空函数摆在那里;
- 不推荐,全局范围重定义 new,风险是非常大的;
类层次范围(推荐):
1,重载 new/delete,不抛出任何异常;
2,失败了返回空指针;
单次动态内存分配:
1,使用 nothrow 参数,指明 new 不抛出异常;
2,失败了返回空指针;
Test Code
#include <iostream>
#include <new>
#include <cstdlib>
#include <exception>
using namespace std;
class Test
{
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; // 这里当没有加上 throw() 时,编译器显示: warning: 'operator new' must not return NULL unless it is declared 'throw()' (or -fcheck-new is in effect);
}
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 my_new_handler()
{
cout << "void my_new_handler()" << endl;
}
/* 证明 new_handler() 函数存在 */
void ex_func_1()
{
/* 定义 func 变量,其类型为 new_handler 类型,C++ 中 new_handler 是一个预定义的函数指针,指向的函数类型是 void(*)(),调用 set_new_handler() 是将自定义的 my_new_handler() 处理函数设置进去,设置了自定义处理函数后,原来的处理函数就会作为返回值返回到 func,这点和上一节的不同,上一节是返回自定义的处理函数,这一节是返回原来的预定义处理函数; */
new_handler func = set_new_handler(my_new_handler);
try
{
cout << "func = " << func << endl;
if( func ) // 加上 if() 处理语句是因为默认的情况下面可能是没有处理函数的,此时 func 为空;
{
func();
}
}
catch(const bad_alloc&) // 想证明默认的 new_handler() 处理函数确实是要抛出 bad_alloc 异常;
{
cout << "catch(const bad_alloc&)" << endl;
}
}
void ex_func_2()
{
Test* pt = new Test();
cout << "pt = " << pt << endl;
delete pt;
pt = new Test[5];
cout << "pt = " << pt << endl;
delete[] pt;
}
/* 如何在单次申请的时候,告诉编译器,不管结果是什么,都不要抛出异常,如果说申请失败了,直接返回空指针 */
void ex_func_3()
{
/* 这个语句是 C++ 标准语法,只是之前没见过而已 */
int* p = new(nothrow) int[10]; // 现在进行动态内存申请,但是不管结果有没有成功,都不要抛出异常,结果失败,直接返回空;
// ... ...
delete[] p;
/* 上面 new 的写法也可以写成下面的形式 */
int bb[2] = {0};
struct ST
{
int x;
int y;
};
ST* pt = new(bb) ST(); // 把 ST 对象创建到 bb[2] 的栈空间中去,即在指定的位置创建一个对象,括号的作用是向编译器指明要在指定的地址上面创建一个对象出来;
/* 对创建的对象赋值 */
pt->x = 1;
pt->y = 2;
/* 这里的打印想证明上面创建的对象确实存在 bb[2] 空间当中的 */
cout << bb[0] << endl; // 打印 1
cout << bb[1] << endl; // 打印 2
pt->~ST(); // 显示的调用析构函数,因为我们指定了穿件对象的空间,这时必须显示手动调用析构函数;
}
int main(int argc, char *argv[])
{
// ex_func_1();
// ex_func_2();
// ex_func_3();
return 0;
}
summary
tips:
不同的编译器在动态内存分配上的实现细节不同;
本文内容参考狄泰软件学院教程
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。