new/delete相关

std::bad_alloc与srd::nothrow

使用new申请内存时,如果申请内存失败,会抛出std::bad_alloc异常,该异常的默认处理是终止进程。
当然我们可以自己对该异常进行处理:

  • try/catch:收到bad_alloc异常表示资源申请失败,进行自定义异常处理
  • 使用std::nothrow,不让new运算符抛出异常,我们通过检查指针是否等于nullptr判断内存申请是否成功

针对上面两种处理的示例如下:

  1. //使用new申请内存时,nothrow可以控制在申请失败时,是否需要抛出异常
  2. #include <iostream>
  3. #include <new> //用于使用nothrow
  4. int main()
  5. {
  6. //自定义异常处理
  7. try {
  8. while (true) {
  9. new int[100000000ul]; // 抛出重载
  10. }
  11. } catch (const std::bad_alloc& e) {
  12. std::cout << e.what() << '\n';
  13. }
  14. while (true) {
  15. int* p = new(std::nothrow) int[100000000ul]; // 不抛出异常
  16. if (p == nullptr) {//直接判断指针
  17. std::cout << "Allocation returned nullptr\n";
  18. break;
  19. }
  20. }
  21. }

保存指针的数组

普通类型的数组,使用new[]/delete[]即可申请是否内存。但如果数组里保存的是指针,那么delete[]仅仅是把数组的空间被释放了,而数组内指针指向的内存地址仍然存在,而且再也访问不到了。
这时候就需要先遍历指针,把最后指向的内存先释放,然后一层一层释放上层内存,直到最后数组内存被释放。

下面例子是用new动态申请了一个二维数组,用二维指针指向基地址。可以借助下面的图理解一下内存分布:
image.png

  1. //对于保存指针的数组,delete时需要遍历一个一个delete[],最后delete[]整个数组
  2. #include <iostream>
  3. using namespace std;
  4. int main()
  5. {
  6. const int size{2};
  7. int **board{new int *[size]};
  8. for (int i = 0; i < size; i++)
  9. {
  10. board[i] = new int[size]{i};
  11. }
  12. //开始delete内存
  13. for (int i = 0; i < size; i++)
  14. {
  15. delete[] board[i]; //delete内层地址
  16. board[i] = nullptr;
  17. }
  18. delete[] board;//delete最上层地址
  19. board = nullptr;
  20. return 0;
  21. }

太复杂难理解而且容易出错了,所以推荐使用**std::array****std::vector**模板实现相同的功能,不用考虑内存申请释放,避免像上面例子一样使用原始的数组

内存泄漏检测工具

MSVC

MSVC 调试库内建了内存泄漏检查工具,默认情况下没有开启,可以通过如下步骤开始

  1. 在代码开头加入一下三行代码

    1. #define _CRTDGB_MAP_ALLOC
    2. #include <cstdlib>
    3. #include <crtdbg.h>
  2. 重新定义new运算符

    1. #ifdef _DEBUG
    2. #ifndef _DGB_NEW
    3. #define DGB_NEW new ( _NORMAL_BLOCK, __FILE__,__LINE__)
    4. #define new DGB_NEW
    5. #endif // _DGB_NEW
    6. #endif // _DEBUG
  3. 开启内存检测,在main函数的第一行加入以下代码

    1. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  4. 之后开始F5调试程序,即可在调试窗口显示检测到的问题[

](https://blog.csdn.net/Timeinsist/article/details/113154929)

使用示例:

  1. //存在内存泄漏的代码示例,用MSVC调试检测内存错误
  2. #include <iostream>
  3. //引入msvc调试头文件
  4. #define _CRTDGB_MAP_ALLOC
  5. #include <cstdlib>
  6. #include <crtdbg.h>
  7. //重定义new运算符
  8. #ifdef _DEBUG
  9. #ifndef _DGB_NEW
  10. #define DGB_NEW new (_NORMAL_BLOCK, __FILE__, __LINE__)
  11. #define new DGB_NEW
  12. #endif // _DGB_NEW
  13. #endif // _DEBUG
  14. class Simple
  15. {
  16. public:
  17. //构造和析构没有问题,new/delete成对出现
  18. Simple() : mIntPtr(new int[1024]) {}
  19. ~Simple() { delete mIntPtr; }
  20. void DoSomeThing(Simple *&outParam)
  21. {
  22. outParam = new Simple();
  23. //这里有问题,outParam直接指向新创建的对象,原来传入的对象没有释放
  24. }
  25. protected:
  26. int *mIntPtr;
  27. };
  28. int main(int argc, char **argv)
  29. {
  30. //开启内存检测
  31. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  32. auto simplePtr = new Simple;
  33. simplePtr->DoSomeThing(simplePtr);
  34. std::cout << "Hello world" << std::endl;
  35. return 0;
  36. }

visual studio调试窗口结果:有两处内存泄漏,line 26和line 39申请的两个内存都没有释放。
image.png

Linux with Valgrind

4 Valgrind程序动态分析工具

智能指针

5 智能指针