当前位置: 首页 > news >正文

C和C++的内存管理 续篇

        上文提到了内存管理的基本方法,本文则从更底层的层面来学习new/delete的有关知识。

operator newoperator delete函数

        与malloc/free不同的是,new和delete不是函数,newdelete是用户进行动态内存申请和释放的操作符operator new operator delete是 系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。

operator new operator delete的实现

void * __CRTDECL operator new ( size_t size ) _THROW1 ( _STD bad_alloc )
{
// try to allocate size bytes
// 该函数实际通过 malloc 来申请空间
void * p ;
while (( p = malloc ( size )) == 0 )
if ( _callnewh ( size ) == 0 )
    {
       
// 如果申请内存失败了,这里会抛出 bad_alloc 类型异常
       
static const std::bad_alloc nomem ;
       
_RAISE ( nomem );
    }
return ( p );
}
/*
operator delete: 该函数最终是通过 free 来释放空间的
*/
void operator delete ( void * pUserData )
{
   
_CrtMemBlockHeader * pHead ;
   
RTCCALLBACK ( _RTC_Free_hook , ( pUserData , 0 ));
   
if ( pUserData == NULL )
       
return ;
   
_mlock ( _HEAP_LOCK );   /* block other threads */
   
__TRY
       
/* get a pointer to memory block header */
       
pHead = pHdr ( pUserData );
         
/* verify block type */
       
_ASSERTE ( _BLOCK_TYPE_IS_VALID ( pHead -> nBlockUse ));
       
_free_dbg ( pUserData , pHead -> nBlockUse );
   
__FINALLY
       
_munlock ( _HEAP_LOCK );   /* release other threads */
   
__END_TRY_FINALLY
   
return ;
}
        不难看出,operator new是通过malloc申请空间的,成功则直接返回, 否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。而operator delete是通过free释放空间的。

newdelete的实现原理

内置类型

  • new:使用operator new分配内存(类似malloc),失败时抛出bad_alloc异常,处理单个元素。

  • delete:使用operator delete释放内存(类似free)。

  • new[]/delete[]处理连续空间,new[]分配后不调用构造函数(内置类型无构造),delete[]直接释放。

自定义类型 

  • new

    • 调用operator new分配内存。

    • 在分配的内存上调用对象的构造函数

  • delete

    • 调用对象的析构函数,清理资源。

    • 调用operator delete释放内存。

  • new T[N]/delete[]

    • new[]:调用operator new[]分配连续内存(内部通过operator new完成),对每个元素调用构造函数。

    • delete[]:对每个元素调用析构函数,再调用operator delete[]释放内存(内部通过operator delete实现)。

定位new表达式

        在已分配的原始内存空间中调用构造函数初始化一个对象(常在内存池中使用来初始化分配的空间)
new (place_address) type 或者 new (place_address) type(initializer-list)
place_address 必须是一个指针, initializer-list 是类型的初始化列表

C/C++中内存管理方式的主要区别

1.malloc/free是函数,new/delete是操作符。

2.malloc只能开辟空间,new还可以直接初始化对象,malloc开辟失败返回null,而new会抛异常

3.malloc返回值为void*类型,使用时根据实际元素类型要类型转换,而new后直接跟类型即可。

4.malloc/free 需严格手动管理释放,易遗漏导致内存泄漏。new/delete 可通过智能指针(如 std::unique_ptrstd::shared_ptr)自动管理生命周期,降低泄漏风险。

5.new/delete 是 C++ 特性,强制与构造函数/析构函数绑定,更适合面向对象编程。

核心原则

  • C++ 中优先使用 new/delete 管理动态内存,确保对象生命周期安全。

  • 避免混用 malloc/free 和 new/delete,严格配对使用。

  • 利用智能指针和 RAII 机制,减少手动内存管理错误。

内存泄漏

        内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。

分类

堆内存泄漏(Heap leak)
        堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new 等从堆中分配的一
块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分
内存没有被释放,那么以后这部分空间将无法再被使用,就会产生 Heap Leak
系统资源泄漏
        指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放
掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

 

常见场景

1.动态内存未释放

使用 new/malloc 分配内存后,忘记调用 delete/free

void func() {
    int* ptr = new int[100]; // 未释放
    // ... 
}

2.异常导致资源未释放

在 new 和 delete 之间发生异常,导致释放代码未执行

void loadData() {
    int* data = new int[1024];
    processData(); // 若此处抛出异常,data 未释放!
    delete[] data;
}

3.容器中的指针未清理

容器(如 vector<Object*>)存储指针,容器销毁时未手动释放指针指向的内存

std::vector<MyClass*> vec;
vec.push_back(new MyClass()); // 容器销毁时未 delete

4.循环引用(智能指针)

使用 std::shared_ptr 时,对象互相引用,导致引用计数无法归零

class A {
    std::shared_ptr<B> b_ptr;
};
class B {
    std::shared_ptr<A> a_ptr; // A 和 B 互相持有 shared_ptr,导致内存泄漏
};

5.第三方资源未释放

文件句柄、数据库连接、网络资源等未关闭

FILE* file = fopen("data.txt", "r");
// ... 未调用 fclose(file)

注意事项

那么针对这些问题,我们需要注意以下几点

  1. 优先使用智能指针

    • 方法

      • 使用 std::unique_ptr 或 std::shared_ptr 管理动态内存。

      • 对于循环引用,用 std::weak_ptr 打破强引用。

    • 示例

      std::unique_ptr<int> ptr = std::make_unique<int>(42); // 自动释放
  2. 遵循 RAII 原则

    • 方法:将资源(内存、文件句柄等)封装在对象中,利用析构函数自动释放。

    • 示例

      class FileHandler {
      public:
          FileHandler(const char* path) { file_ = fopen(path, "r"); }
          ~FileHandler() { if (file_) fclose(file_); }
      private:
          FILE* file_;
      };
  3. 确保异常安全

    • 方法

      • 在可能抛出异常的代码中使用智能指针。

      • 使用 try-catch 确保异常后资源释放。

    • 示例

      try {
          auto ptr = std::make_unique<Resource>();
          riskyOperation(); // 可能抛出异常
      } catch (...) {
          // 异常时 ptr 会自动释放
      }
  4. 避免手动管理内存

    • 方法

      • 使用标准库容器(如 vectorstring)替代裸指针数组。

      • 使用 std::string 而非 char*

    • 示例

      std::vector<int> data(100); // 无需手动释放
  5. 工具检测与调试

    • 方法

      • 使用 Valgrind(Linux)或 Dr. Memory(Windows)检测内存泄漏。

      • 开启编译器工具(如 GCC/Clang 的 -fsanitize=address)。

    • 示例

      valgrind --leak-check=full ./your_program

        到这里,关于C/C++中的动态内存管理的知识就告一段落了,这些知识非常重要,是C++中值得关注的问题,尤其是关于内存泄露的避免,C++学习者们应该要注意到的。

相关文章:

  • C#实现本地Deepseek模型及其他模型的对话v1.4
  • 在线商城服务器
  • 统计建模攻略|一文了解统计建模和其他建模比赛的区别
  • CentOS 7系统初始化及虚拟化环境搭建手册
  • 论文阅读 GMM-JCSFE Model(EEG Microstate)
  • 自然语言处理初学者指南
  • 单机DeepSeek做PPT,YYDS!
  • 【Pytorch Transformers Fine-tune】使用BERT进行情感分类任务微调
  • 如何构建高效数据流通交易体系
  • centos操作系统上传和下载百度网盘内容
  • SQL-留存率
  • 云原生可观测性体系:数字世界的神经感知网络
  • 【亲测有效】Electron打包的应用不支持mac os 10.11问题,Electron在mac os 10.11无法安装问题
  • 【JavaEE】创建SpringBoot第一个项目,Spring Web MVC⼊⻔,从概念到实战的 Web 开发进阶之旅
  • 深入理解 Linux 中的 -h 选项:让命令输出更“人性化”
  • 【深度学习】自定义层
  • std::ranges::views::counted
  • SCT2632-3A持续输出电流,输入电压范围:4.2V-60V,降压DCDC转换器
  • 神经网络机器学习中说的过拟合是什么意思
  • 【论文笔记】Best Practices and Lessons Learned on Synthetic Data for Language Models
  • drupal wordpress网站/网络黄页推广软件哪个好用
  • 社交网站开发语言/厦门seo小谢
  • 做网站建设的平台/企业营销策划方案范文
  • wordpress做教育网站/西安官网seo技术
  • 天津做流产五洲网站/国内能用的搜索引擎
  • 宝塔搭建网站/随州seo