1,问题:

1,动态内存申请一定成功吗?

1,不一定成功;

2,常见的动态内存分配代码:

1,C 代码:

 int* p = (int*)malloc( * sizeof(int));

 if( p !=  NULL )
{
// ... ...
}

1,这种写法合理;

2,C++ 代码:

 int* p = new int[];

 if( p != NULL )
{
// ... ...
}

1,古代编译器这种写法合理;

2,现代编译器这种写法就不合理,申请成功时,此语句没有任何意义,申请失败后,就会抛出一个标准库中的异常对象,程序就不会向下执行到  if() 语句;

3,如果用的是一款现代的 C++ 编译器,new 的结果无论成功或者失败,根本用不着使用 if() 判断语句;

3,申请内存失败时:

1,malloc 函数申请失败时返回 NULL 值;

2,new 关键字申请失败时(根据编译器的不同):

1,返回 NULL 值;

1,古代编译器兼容 C 中方式,返回 NULL 值;

2,抛出 std::bad_alloc 异常;

1,近代编译器不会返回 NULL 值,而是抛出一个标准库中的 std::bad_alloc 异常;

4,问题:

1,new 语句中的异常是怎么抛出来的?

5,new 关键字在 C++ 规范中的标准行为:

1,在堆空间申请足够大的内存;

1,成功:

1,在获取的空间中调用构造函数创建对象;

2,返回对象的地址;

2,失败:

1,C++ 规范定义要抛出 std::bad_alloc 异常;

2,new 在分配空间时:

1,如果空间不足,会调用全局的 new_handler() 函数(见 9 中例一);

1,调用 new_handler() 函数的意义在于我们可以有机会做一些处理,使得有更多的空间可以空出来;

2,整理空间、空出足够的内存 C++ 编译器是不知道的,这件事情具体的平台具体讨论,于是 C++ 标准规范就给了默认的 new_handler() 实现,即抛出异常;

2,new_handler() 函数中抛出 std::bad_alloc 异常;

3,可以自定义 new_handler() 函数:

1,处理默认的 new 内存分配失败的情况;

2,C++ 平台不能整理空间、空出足够的内存,那么就交给我们自己来定义;

6,new_handler() 的定义和使用:

1,代码示例:

 void my_new_handler()
{
cout << "No enough memory";
cout << endl; exit(); // 内存不足了,就把当前的程序结束了;
}

1,实际产品开发中,一般会尝试在这个函数中进行内存的整理,整理后期望有更多的堆空间空出来,然后满足当前程序对内存的需求;

2,也可以内存不足自己结束程序;

3,友好的方式是抛一个异常,然后进行异常处理,这是 C++ 默认的实现方式;

2,使用:

 int main(int argc, char* argv[])
{
set_new_handler(my_new_handler); // 告诉 C++ 编译器;可以设置自定义的处理函数,处理堆空间不足的情况; // ... ... return ;
}

7,问题:

1,如果跨编译器统一 new 的行为,提高代码移植性?

1,无论在任何情况下,申请失败都返回空或者抛出异常;

2,为了兼顾古代编译器,一般做法是自定义 new,使得 new 在申请堆空间失败的时候,直接返回空指针,而不抛出异常;

8,解决方案:

1,全局范围(不推荐):

1,重新定义 nwe/delete 的实现,不抛出任何异常;

2,自定义 new_handler() 函数,不抛出任何异常;

1,空函数摆在那里;

2,见本文 10.2.2.1 分析,这个方案对 VS 2010 编译器是有用的;

3,不推荐,全局范围重定义 new,风险是非常大的;

2,类层次范围(推荐)(见本文 9 中例二):

1,重载 new/delete,不抛出任何异常;

2,失败了返回空指针;

3,单次动态内存分配(见 本文9 中例三):

1,使用 nothrow 参数,指明 new 不抛出异常;

2,失败了返回空指针;

9,动态内存申请编程实验:

 #include <iostream>
#include <new>
#include <cstdlib>
#include <exception> using namespace std; class Test
{
int m_value;
public:
Test()
{
cout << "Test()" << endl; m_value = ;
} ~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[]; cout << "pt = " << pt << endl; delete[] pt;
} /* 如何在单次申请的时候,告诉编译器,不管结果是什么,都不要抛出异常,如果说申请失败了,直接返回空指针 */
void ex_func_3()
{
/* 这个语句是 C++ 标准语法,只是之前没见过而已 */
int* p = new(nothrow) int[]; // 现在进行动态内存申请,但是不管结果有没有成功,都不要抛出异常,结果失败,直接返回空; // ... ... delete[] p; /* 上面 new 的写法也可以写成下面的形式 */ int bb[] = {}; struct ST
{
int x;
int y;
}; ST* pt = new(bb) ST(); // 把 ST 对象创建到 bb[2] 的栈空间中去,即在指定的位置创建一个对象,括号的作用是向编译器指明要在指定的地址上面创建一个对象出来; /* 对创建的对象赋值 */
pt->x = ;
pt->y = ; /* 这里的打印想证明上面创建的对象确实存在 bb[2] 空间当中的 */
cout << bb[] << endl; // 打印 1
cout << bb[] << endl; // 打印 2 pt->~ST(); // 显示的调用析构函数,因为我们指定了穿件对象的空间,这时必须显示手动调用析构函数;
} int main(int argc, char *argv[])
{
// ex_func_1();
// ex_func_2();
// ex_func_3(); return ;
}

1,ex_func_1() 打印结果:

1,g++ 编译器:

func = 0;说明默认的情况下,g++ 编译器并没有一个全局的 new_handler() 处理函数;

2,VS 2010 编译器:

func = 00000000;说明默认的情况下,VS 2010 编译器并没有一个全局的 new_handler() 处理函数;

3,BCC 编译器:

func = 00401474

catch(const bad_alloc&);说明 BCC 的实现当中,确实是有一个全局 new_handler() 函数,它在调用后确实抛出了一个 bad_alloc 异常;

2,第一个打印结果说明不同的 C++ 编译器 new 的行为是有一点不一样的,具     体在 new 失败时;

3,ex_func_2() 打印 new 的结果:

1,g++ 编译器:

operator new: 4

Test()

段错误;因为 new 的重载函数返回空,于是 C++ 编译器在空地址上面调用构造函数创建对象,并且在构造函数中做了一个赋值操作,这等价于对 0 地址处进行赋值,所以段错误;

2,VS 2010 编译器:

operator new: 4

pt = 00000000;这款编译器中,如果 new 的结果返回为空,是不会调用构造函数的;

3,BCC 编译器:

operator new: 4

pt = 00000000;这款编译器中,如果 new 的结果返回为空,是不会调用构造函数的;

4,不管哪款编译器,申请动态内存失败,直接返回空指针,不要干其它多余操作,则对 new 和 new[] 的重载进行异常规格说明,说明无论如何都不会扔出异常;

1,g++ 编译器:

operator new: 4

pt = 0;和 VS 2010 以及 BCC 编译器行为统一了;

5,ex_func_2() 打印 new[] 的结果:

1,g++ 编译器:

operator new[]: 24

pt = 0

2,VS 2010 编译器:

operator new[]: 24

pt = 00000000

3,BCC 编译器:

operator new[]: 24

pt = 00000000

operator delete[]: 00000000

6,ex_func_3() 打印结果:

1,g++ 编译器:

1

2

2,VS 2010 编译器:

1

2

3,BCC 编译器:

1

2

7,new 关键字可以在指定的空间上面创建对象,如果显示的指定穿件对象的内存,就要显示的手动调用析构函数;

8,三款编译器的行为都一样了;

10,实验结论:

1,不是所有的编译器都遵循 C++ 的标准规范;

2,编译器可能重定义 new 的实现,并在实现中抛出 bad_alloc 异常;

1,虽然现代的编译器在 new 失败的时候会抛出一个 bad_alloc 异常,但是这个异常不一定就是在默认的 new_handler() 函数里面抛出来的,只有(上述三款编译器) BCC 编译器设置了默认的全局的  new_handler() 函数;

2,分析 VS 2010 new 如何实现,以观察 new_handler() 如何实现:

3,编译器的默认实现中,可能没有设置全局的 new_handler() 函数;

4,对于移植性要求高的代码,需要考虑 new 的具体细节;

11,小结:

1,不同的编译器在动态内存分配上的实现细节不同;

2,malloc 函数在内存申请失败时返回 NULL 值;

3,new 关键字在内存申请失败时:

1,可能返回 NULL 值;

2,可能抛出 bad_alloc 值;

C++中动态内存申请的结果的更多相关文章

  1. C++函数中,两个自动释放内存的动态内存申请类

    最近做一个事情,实现一个流程交互,其中主交互流程函数中,涉及较多的内存申请, 而健康的函数,都是在函数退出前将手动申请不再需要的内存释放掉, 使用很多方法,都避免不了较多的出错分支时,一堆的if fr ...

  2. C++解析-外传篇(3):动态内存申请的结果

    0.目录 1.动态内存申请一定成功吗? 2.new_handler() 函数 3.小结 1.动态内存申请一定成功吗? 问题: 动态内存申请一定成功吗? 常见的动态内存分配代码: C代码: C++代码: ...

  3. C语言中动态内存分配的本质是什么?

    摘要:C语言中比较重要的就是指针,它可以用来链表操作,谈到链表,很多时候为此分配内存采用动态分配而不是静态分配. 本文分享自华为云社区<[云驻共创]C语言中动态内存分配的本质>,作者: G ...

  4. rt-thread中动态内存分配之小内存管理模块方法的一点理解

    @2019-01-18 [小记] rt-thread中动态内存分配之小内存管理模块方法的一点理解 > 内存初始化后的布局示意 lfree指向内存空闲区首地址 /** * @ingroup Sys ...

  5. C语言中动态内存的分配(malloc,realloc)

    动态内存分配:根据需要随时开辟,随时释放的内存分配方式.分配时机和释放时机完全由程序员决定,由于没有数据声明,这部分空间没有名字.无法像使用变量或数组那样通过变量名或数组名引用其中的数据,只能通过指针 ...

  6. c/c++中动态内存分配处理字符串的细节问题

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <malloc.h&g ...

  7. 转:内核中的内存申请:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages

    在内核模块中申请分配内存需要使用内核中的专用API:kmalloc.vmalloc.kzalloc.kcalloc.get_free_pages;当然,设备驱动程序也不例外;对于提供了MMU功能的处理 ...

  8. 内核中的内存申请:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages【转】

    转自:http://www.cnblogs.com/yfz0/p/5829443.html 在内核模块中申请分配内存需要使用内核中的专用API:kmalloc.vmalloc.kzalloc.kcal ...

  9. 【转】内核中的内存申请:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages

    转自:https://www.cnblogs.com/yfz0/p/5829443.html 在内核模块中申请分配内存需要使用内核中的专用API:kmalloc.vmalloc.kzalloc.kca ...

随机推荐

  1. go web编程——实现一个简单分页器

    在go web编程中,当需要展示的列表数据太多时,不可避免需要分页展示,可以使用Go实现一个简单分页器,提供各个数据列表展示使用.具体需求:1. 可展示“首页”和“尾页”.2. 可展示“上一页”和“下 ...

  2. vue-devtools工具的安装

    vue-devtools是一款基于chrome游览器的插件,用于调试vue应用,这可以极大地提高我们的调试效率.接下来我们就介绍一下vue-devtools的安装 1.登录github,获取到vue- ...

  3. tornado ioloop current和instance的一些区别

    import tornado.ioloop # 此时_current没有instance print dir(tornado.ioloop.IOLoop._current) # 通过instance ...

  4. 【归纳】Layui table.render里的json后台传入

    在使用Layui的table元素时,传入的json的数据格式是有其自身定义的,需要另外添加一些字符,以正确传入. 为了传入符合前端格式的数据: table.render({ elem: '#test' ...

  5. MFC程序执行过程剖析(转)

    一 MFC程序执行过程剖析 1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用 ...

  6. ivew 【provide/inject] 页面刷新实现reload

    1.App.vue <template> <div id="app"> <router-view v-if="isRouterAlive&q ...

  7. JavaScript判断对象是否相等

    实现一. var obj = {a:'a'},obj1 = {b:'b'},obj2 = {a:'a'};就是使用JSON.stringify()先把对象转化成字符串,这样就可以啦 console.l ...

  8. 前后端分离下的CAS跨域流程分析

    写在最前 前后端分离其实有两类: 开发阶段使用dev-server,生产阶段是打包成静态文件整个放入后端项目中. 开发阶段使用dev-server,生产阶段是打包成静态文件放入单独的静态资源服务器中, ...

  9. SDOI2018凉凉记

    好久没有更博客了...因为我在颓废学习.. SDOI一轮结束了...我也该回来学地生了... 凉凉 ————————————————我是分割线———————————————— Day0 愉快感冒的Da ...

  10. 指针使用const修饰总结

    1 double rates[5] = {1, 2, 3, 4, 5}; const double * pd = rates; 被pd指向的值不可改变,比如,不允许*pd = 20 但是pd的指向改变 ...