0.目录

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

2.new_handler() 函数

3.小结

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

问题:

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

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

C代码:



C++代码:

必须知道的事实!

  • malloc函数申请失败时返回NULL值
  • new关键字申请失败时(根据编译器的不同)
    1. 返回NULL值
    2. 抛出 std::bad_alloc 异常

问题:

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

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

  • 在堆空间申请足够大的内存

    1. 成功:

      1. 在获取的空间中调用构造函数创建对象
      2. 返回对象的地址
    2. 失败:
      1. 抛出 std::bad_alloc 异常
  • new在分配内存时
    1. 如果空间不足,会调用全局的 new_handler() 函数
    2. new_handler() 函数中抛出 std::bad_alloc 异常
  • 可以自定义 new_handler() 函数
    1. 处理默认的new内存分配失败的情况

2.new_handler() 函数

new_handler() 的定义和使用:

问题:

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

解决方案:

  • 全局范围(不推荐)

    1. 重新定义 new / delete 的实现,不抛出任何异常
    2. 自定义 new_handler() 函数,不抛出任何异常
  • 类层次范围
    1. 重载 new / delete,不抛出任何异常
  • 单次动态内存分配
    1. 使用 nothrow 参数,指明 new 不抛出异常

示例1——证明存在 new_handler() 函数:

  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4. class Test
  5. {
  6. int m_value;
  7. public:
  8. Test()
  9. {
  10. cout << "Test()" << endl;
  11. m_value = 0;
  12. }
  13. ~Test()
  14. {
  15. cout << "~Test()" << endl;
  16. }
  17. void* operator new (unsigned long size)
  18. {
  19. cout << "operator new: " << size << endl;
  20. // return malloc(size);
  21. return NULL;
  22. }
  23. void operator delete (void* p)
  24. {
  25. cout << "operator delete: " << p << endl;
  26. free(p);
  27. }
  28. void* operator new[] (unsigned long size)
  29. {
  30. cout << "operator new[]: " << size << endl;
  31. return malloc(size);
  32. }
  33. void operator delete[] (void* p)
  34. {
  35. cout << "operator delete[]: " << p << endl;
  36. free(p);
  37. }
  38. };
  39. void my_new_handler()
  40. {
  41. cout << "void my_new_handler()" << endl;
  42. }
  43. void ex_func_1()
  44. {
  45. new_handler func = set_new_handler(my_new_handler);
  46. try
  47. {
  48. cout << "func = " << func << endl;
  49. if( func )
  50. {
  51. func();
  52. }
  53. }
  54. catch(const bad_alloc&)
  55. {
  56. cout << "catch(const bad_alloc&)" << endl;
  57. }
  58. }
  59. int main(int argc, char *argv[])
  60. {
  61. ex_func_1();
  62. return 0;
  63. }

运行结果为:

  1. [root@bogon Desktop]# g++ test.cpp
  2. test.cpp: In static member function static void* Test::operator new(long unsigned int)’:
  3. test.cpp:28: warning: operator new must not return NULL unless it is declared throw()’ (or -fcheck-new is in effect)
  4. [root@bogon Desktop]# ./a.out
  5. func = 0

(在g++编译器中没有设置这个全局的 new_handler() 函数,bcc编译器中实现了这个全局的 new_handler() 函数。)

示例2——动态内存申请失败的结果:

  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4. class Test
  5. {
  6. int m_value;
  7. public:
  8. Test()
  9. {
  10. cout << "Test()" << endl;
  11. m_value = 0;
  12. }
  13. ~Test()
  14. {
  15. cout << "~Test()" << endl;
  16. }
  17. void* operator new (unsigned long size)
  18. {
  19. cout << "operator new: " << size << endl;
  20. // return malloc(size);
  21. return NULL;
  22. }
  23. void operator delete (void* p)
  24. {
  25. cout << "operator delete: " << p << endl;
  26. free(p);
  27. }
  28. void* operator new[] (unsigned long size)
  29. {
  30. cout << "operator new[]: " << size << endl;
  31. return malloc(size);
  32. }
  33. void operator delete[] (void* p)
  34. {
  35. cout << "operator delete[]: " << p << endl;
  36. free(p);
  37. }
  38. };
  39. void ex_func_2()
  40. {
  41. Test* pt = new Test();
  42. cout << "pt = " << pt << endl;
  43. delete pt;
  44. }
  45. int main(int argc, char *argv[])
  46. {
  47. ex_func_2();
  48. return 0;
  49. }

运行结果为:

  1. [root@bogon Desktop]# g++ test.cpp
  2. test.cpp: In static member function static void* Test::operator new(long unsigned int)’:
  3. test.cpp:28: warning: operator new must not return NULL unless it is declared throw()’ (or -fcheck-new is in effect)
  4. [root@bogon Desktop]# ./a.out
  5. operator new: 4
  6. Test()
  7. Segmentation fault (core dumped)

(g++编译器中报错:段错误。不同的编译器报错信息不同。)

示例3——统一不同编译器动态内存申请失败后的行为:

  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4. class Test
  5. {
  6. int m_value;
  7. public:
  8. Test()
  9. {
  10. cout << "Test()" << endl;
  11. m_value = 0;
  12. }
  13. ~Test()
  14. {
  15. cout << "~Test()" << endl;
  16. }
  17. void* operator new (unsigned long size) throw()
  18. {
  19. cout << "operator new: " << size << endl;
  20. // return malloc(size);
  21. return NULL;
  22. }
  23. void operator delete (void* p)
  24. {
  25. cout << "operator delete: " << p << endl;
  26. free(p);
  27. }
  28. void* operator new[] (unsigned long size) throw()
  29. {
  30. cout << "operator new[]: " << size << endl;
  31. // return malloc(size);
  32. return NULL;
  33. }
  34. void operator delete[] (void* p)
  35. {
  36. cout << "operator delete[]: " << p << endl;
  37. free(p);
  38. }
  39. };
  40. void ex_func_2()
  41. {
  42. Test* pt = new Test();
  43. cout << "pt = " << pt << endl;
  44. delete pt;
  45. pt = new Test[5];
  46. cout << "pt = " << pt << endl;
  47. delete[] pt;
  48. }
  49. int main(int argc, char *argv[])
  50. {
  51. ex_func_2();
  52. return 0;
  53. }

运行结果为:

  1. [root@bogon Desktop]# g++ test.cpp
  2. [root@bogon Desktop]# ./a.out
  3. operator new: 4
  4. pt = 0
  5. operator new[]: 28
  6. pt = 0

显示的调用析构函数

示例4——让编译器申请失败后返回空指针而不是抛出异常:

  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4. class Test
  5. {
  6. int m_value;
  7. public:
  8. Test()
  9. {
  10. cout << "Test()" << endl;
  11. m_value = 0;
  12. }
  13. ~Test()
  14. {
  15. cout << "~Test()" << endl;
  16. }
  17. void* operator new (unsigned long size) throw()
  18. {
  19. cout << "operator new: " << size << endl;
  20. // return malloc(size);
  21. return NULL;
  22. }
  23. void operator delete (void* p)
  24. {
  25. cout << "operator delete: " << p << endl;
  26. free(p);
  27. }
  28. void* operator new[] (unsigned long size) throw()
  29. {
  30. cout << "operator new[]: " << size << endl;
  31. // return malloc(size);
  32. return NULL;
  33. }
  34. void operator delete[] (void* p)
  35. {
  36. cout << "operator delete[]: " << p << endl;
  37. free(p);
  38. }
  39. };
  40. void ex_func_3()
  41. {
  42. int* p = new(nothrow) int[10];
  43. // ... ...
  44. delete[] p;
  45. int bb[2] = {0};
  46. struct ST
  47. {
  48. int x;
  49. int y;
  50. };
  51. ST* pt = new(bb) ST(); // new():在指定空间上创建对象
  52. pt->x = 1;
  53. pt->y = 2;
  54. cout << bb[0] << endl;
  55. cout << bb[1] << endl;
  56. pt->~ST(); // 显示的调用析构函数
  57. }
  58. int main(int argc, char *argv[])
  59. {
  60. ex_func_3();
  61. return 0;
  62. }

运行结果为:

  1. [root@bogon Desktop]# g++ test.cpp
  2. [root@bogon Desktop]# ./a.out
  3. 1
  4. 2

实验结论:

  • 不是所有的编译器都遵循C++的标准规范
  • 编译器可能重定义 new 的实现,并在实现中抛出 bad_alloc 异常
  • 编译器的默认实现中,可能没有设置全局的 new_handler() 函数
  • 对于移植性要求较高的代码,需要考虑 new 的具体细节

3.小结

  • 不同的编译器在动态内存分配上的实现细节不同
  • malloc 函数在内存申请失败时返回NULL值
  • new 关键字在内存申请失败时
    1. 可能返回NULL值
    2. 可能抛出 bad_alloc 异常

C++解析-外传篇(3):动态内存申请的结果的更多相关文章

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

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

  2. C++中动态内存申请的结果

    1,问题: 1,动态内存申请一定成功吗? 1,不一定成功: 2,常见的动态内存分配代码: 1,C 代码: * sizeof(int)); if( p != NULL ) { // ... ... } ...

  3. C++解析(25):关于动态内存分配、虚函数和继承中强制类型转换的疑问

    0.目录 1.动态内存分配 1.1 new和malloc的区别 1.2 delete和free的区别 2.虚函数 2.1 构造函数与析构函数是否可以成为虚函数? 2.2 构造函数与析构函数是否可以发生 ...

  4. C++解析-外传篇(2):函数的异常规格说明

    0.目录 1.异常规格说明 2.unexpected() 函数 3.小结 1.异常规格说明 问题: 如何判断一个函数是否会抛出异常,以及抛出哪些异常? C++提供语法用于声明函数所抛出的异常 异常声明 ...

  5. C++解析-外传篇(1):异常处理深度解析

    0.目录 1.异常的最终处理 2.结束函数terminate() 3.小结 1.异常的最终处理 问题: 如果在main函数中抛出异常会发生什么? 如果异常不处理,最后会传到哪里? 下面的代码的输出什么 ...

  6. C 动态内存申请

    例子: int *p=0; int number=0; scanf("%d",&number); p = (int*)malloc(number*sizeof(int));

  7. 【C/C++开发】C语言 DLL(动态链接库)中申请动态内存释放的问题

    参考:首先,声明一点,凡是使用malloc之类命令动态申请的内存,必须进行释放操作,否则就会发生内存泄漏问题. DLL中申请的内存释放,如果没有做过,很可能会认为是直接在调用程序中释放就可以了,其实不 ...

  8. C语言动态内存的申请和释放

    什么是动态内存的申请和释放? 当程序运行到需要一个动态分配的变量时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存储该变量.当不再使用该变量时,也就是它的生命结束时,要显式释放它所占用的存储空 ...

  9. keil c51的内部RAM(idata)动态内存管理程序

    程序比较简单,但感觉比较有意思,个人认为有一定应用价值,希望大家有更好的思路和方法,互相促进. 程序的基本思路是:在CPU堆栈指针SP以上的RAM区域,通过把堆栈指针SP上移若干个字节,把空出的RAM ...

随机推荐

  1. MySQL入门篇(六)之mysqldump备份和恢复

    一.备份单个数据库 1.备份命令:mysqldump MySQL数据库自带的一个很好用的备份命令.是逻辑备份,导出 的是SQL语句.也就是把数据从MySQL库中以逻辑的SQL语句的形式直接输出或生成备 ...

  2. Nginx入门篇(四)之常用配置解析

    1.Nginx状态信息功能 Nginx的模块当中有一个ngx_http_stub_status_module模块,这个模块主要记录Nginx的基本访问信息,要使用该模块,需要在编译的时候增加http_ ...

  3. Yii2.0 高级模版编写使用自定义组件(component)

    翻译自:http://www.yiiframework.com/wiki/760/yii-2-0-write-use-a-custom-component-in-yii2-0-advanced-tem ...

  4. hive 动态分区插入

    首先需要进行以下设置: set hive.exec.dynamic.partition=true; set hive.exec.dynamic.partition.mode=nonstrict; se ...

  5. html面试题总结

    1.请描述一个网页从开始请求到最终显示的完整过程? 1).在浏览器中输入网址: 2).发送至DNS服务器并获得域名对应的WEB服务器的IP地址: 3).与WEB服务器简历TCP连接: 4).浏览器向W ...

  6. python-编程从入门到实践

    python-编程从入门到实践 1.python文件后缀名: .py 是Python的源码文件,由Python.exe解释. .pyc 是Python的编译文件.pyc 文件往往代替 py 文件发布: ...

  7. vue 跳转到外部 后回跳

    微信  vue 跳转到外部 后回跳  ,比如登陆 授权操作 .需要 路由 先跳转到一个中间页面 后再跳转到授权服务器!而不能跳转前的页面与回跳后的页面相同 不然回跳可能会出现空白 路由不解析.

  8. python的30个编程技巧

     1.原地交换两个数字 x, y =10, 20 print(x, y) y, x = x, y print(x, y) 10 20 20 10 2.链状比较操作符 n = 10 print(1 &l ...

  9. HTML(1)简介

    "超"文本标记语言--HTML 文本,是指书面语言的表现形式. 百度百科 说白了,文本就是你能看得到的字,不论是纸上的还是屏幕上的,都是文本.文本就是用来记录信息一种形式. 那么, ...

  10. Scrum立会报告+燃尽图(十一月二十七日总第三十五次):β阶段最后完善

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2415 项目地址:https://git.coding.net/zhang ...