首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【c++】c/c++内存管理

【c++】c/c++内存管理

作者头像
mosheng
发布2026-01-14 18:25:43
发布2026-01-14 18:25:43
1020
举报
文章被收录于专栏:c++c++

hello~ 很高兴见到大家! 这次带来的是C++中关于内存管理这部分的一些知识点,如果对你有所帮助的话,可否留下你的三连呢? 个 人 主 页: 默|笙

一、c/c++内存管理

c++与c语言的内存分布差不多,如下图:

在这里插入图片描述
在这里插入图片描述
  • *char2 在栈上:char2 是一个数组,它会把后面的字符串常量拷贝一份放到数组中,而数组存放在栈上。
  • *pChar3 在代码段:pChar3是一个指针变量指向字符串常量的首字母地址,而字符串常量存储在代码段。

二、c++内存管理方式

引入:c语言是通过 malloc、calloc、realloc、free来实现动态内存分配的。而C++在保留C风格内存管理的同时,引入了面向对象特性和更安全的动态内存管理机制 new 与 delete操作符,显著提升了代码的安全性和便捷性。

2.1 new操作符

2.1.1 格式
在这里插入图片描述
在这里插入图片描述
  1. 申请单个:类型* 指针名 = 操作符 类型(初始化);
  2. 申请多个:类型* 指针名 = 操作符 类型[对象个数/数组大小]{初始化列表}
2.1.2 讲解
  1. new 操作符主要会完成 2 个工作,一个是申请空间,一个是调用构造函数

  1. 对于内置类型,只用申请空间构造数组,其没有构造函数可调用。
  2. 对于自定义类型,不仅需要为对象分配内存,还会强制调用构造函数
  3. 自定义类型数组里的每个元素都会调用构造函数,有 n个元素,对应就会调用n次构造函数。对于 new[ ]。new 会调用一次。
  4. 关于初始化问题:

未初始化的动态内存:

  1. 基本类型: 默认初始化:动态分配的基本类型变量的值是未定义的,即随机值
  2. 自定义类型 默认初始化:调用默认构造函数。

显式初始化中动态数组的初始化:

  1. 若初始化列表元素数量 < 数组大小,剩余元素值会值初始化
  • 基本类型:剩余元素值初始化为0
  • 自定义类型:调用默认构造函数
  1. 若初始化列表元素数量 > 数组大小,编译错误。
  2. 若完全忽略初始化列表,默认初始化。
  3. 对于自定义类型调用构造函数问题:

自动调用:不传参数,自动调用默认构造函数。 显式调用:无论构造函数的参数个数,都会通过创建匿名对象显式/隐式调用构造函数:

代码语言:javascript
复制
class MyClass
{
public:
	MyClass(int a = 1, int b = 1)
	{
		_a = a;
		_b = b;
	}
private:
	int _a = 0;
	int _b = 0;
};

int main()
{
	MyClass* arr1 = new MyClass[3];      // 为每个元素调用默认构造函数
	MyClass* arr2 = new MyClass[3]{};    // 同上(显式值初始化)
	//显式匿名对象
	MyClass* arr3 = new MyClass[3]{ MyClass(1, 2), MyClass(2, 3) }; // 前两个显式调用构造函数,第三个默认构造
	//隐式匿名对象
	MyClass* arr4 = new MyClass[3]{ {1, 2}, {2, 3} }; 
	//前两个隐式调用构造函数,第三个默认构造
	//这里还有编译器的优化,构造+ 拷贝构造优化为直接构造
	return 0;
}
  1. new 如果申请空间失败,不会返回 nullptr空指针,而是会抛异常。

2.2 delete操作符

2.2.1 格式
  1. delete 指针名 —>完成对单个类型空间的释放。
  2. delete[] 指针名 —>完成对多个类型空间的释放。
  3. 完成释放之后,建议对指针置空。
代码语言:javascript
复制
delete 指针名;
delete[] 指针名;
指针名 = nullptr;
2.2.2 讲解
  1. delete 操作符主要完成两个工作,一个是调用析构函数,一个是释放内存。

对于内置类型:只用释放内存,没有析构函数可调用。 对于自定义类型:先调用析构函数,而后释放内存。

  1. 自定义类型数组里的每个元素都会调用析构函数,有n个元素,对应就会调用n次析构函数。对于 delete[ ]。delete 会调用一次。

三、operator new函数和 operator delete函数

  1. operator new 函数和 operator delete 函数是系统提供的全局函数。
  2. new 在申请内存的时候在底层会调用 operator 函数,delete 在释放内存的时候在底层会调用 operator delete 函数。
  3. operator new函数和 operator delete函数其实可以认为是 malloc 与 free 的重载函数,完成了对 malloc 和 free 的封装工作,它们就是 malloc 与 free 的plus版本。
  • new 表达式不需要显式的指针类型转换,是因为编译器在底层自动完成了这一工作。如果手动调用 operator new 函数,就必须得显式转换了
代码语言:javascript
复制
//通过malloc来申请空间
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
 // try to allocate size bytes
 void *p;
 while ((p = malloc(size)) == 0)
     if (_callnewh(size) == 0)
     {
         // report no memory
         // 如果申请内存失败了,这里会抛出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;
}

四、new 与 delete 实现原理讲解

4.1 实现原理

1.内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是: new/delete申请和释放的是单个元素的空间,new[ ]和delete[ ]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL

2.自定义类型

  1. new 会先调用一次 operator new 函数,再调用一次构造函数。
  2. new[ ] 会先调用一次 operator new 函数(仅一次),再根据元素个数调用构造函数(n次)。
  3. delete 会先调用一次析构函数,再调用一次 operator delete 函数释放内存。
  4. delete[ ] 会根据元素个数调用析构函数(n次),再调用一次 operator delete 函数释放内存(仅一次)。

2.3 搭配问题

  1. new 与 delete 搭配使用,new[ ] 与 delete[ ]搭配使用,malloc 与 free搭配使用。

  1. 混用 new 和 delete[]:
代码语言:javascript
复制
MyClass* obj = new MyClass();  // 分配单个对象
delete[] obj;  // 错误!可能重复调用析构函数(未定义行为),可能导致程序崩溃。

后果:若 MyClass 的析构函数释放资源,重复调用会导致资源破坏。

  1. 混用 new[] 和 delete
代码语言:javascript
复制
MyClass* arr = new MyClass[3];  // 分配数组
delete arr;  // 错误!仅调用第1个元素的析构函数,剩余元素内存泄漏

后果:仅释放数组首元素,剩余对象的析构函数未被调用,导致资源泄漏。

  1. 对内置类型混用 new[ ] 与 free: 因为内置类型不用调用析构函数,某些编译器下这样也可以,但存在巨大风险,而对于像 vs 这样的,是不被允许的。

原因:

在这里插入图片描述
在这里插入图片描述
  • 其实对于 new[ ] 与 delete混用在 vs里面也会发生这个问题。

五、malloc/free 与 new/delete 的区别

特性

malloc/free

new/delete

内存分配本质

标准库函数(stdlib.h)

语言内置操作符(C++ 特性)

类型转换

返回 void*,需手动转换为目标类型

自动推导类型,返回目标指针(如 int*)

构造 / 析构

不调用构造函数和析构函数

自动调用构造函数(new)和析构函数(delete)

内存分配失败处理

返回 NULL,需手动检查

抛出 std::bad_alloc 异常(可通过 std::nothrow 禁用)

数组支持

需手动计算数组大小(如 malloc(n * sizeof(int)))

直接支持 new[] 和 delete[],自动管理数组长度

资源管理

仅管理内存,不处理其他资源

通过构造 / 析构函数自动管理对象生命周期内的所有资源


今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~ 如果可以, 那就让我们共同努力, 一起走下去!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、c/c++内存管理
  • 二、c++内存管理方式
    • 2.1 new操作符
      • 2.1.1 格式
      • 2.1.2 讲解
    • 2.2 delete操作符
      • 2.2.1 格式
      • 2.2.2 讲解
  • 三、operator new函数和 operator delete函数
  • 四、new 与 delete 实现原理讲解
    • 4.1 实现原理
    • 2.3 搭配问题
  • 五、malloc/free 与 new/delete 的区别
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档