C/C++动态内存管理
在 C 和 C++ 中,对于动态内存的管理是一个永恒的话题,C语言采用 x(x)alloc 和 free 来开辟和释放动态内存空间,不同的是,C++ 引入了 new 和 delete 操作符作为新的方式。这二者有什么区别呢?new \ delete 又有哪些高级用法,这些都将在下文一一列出。
malloc \ free \ calloc \ realloc
malloc
1 | void* malloc(size_t size); |
开辟一段 size 字节大小的内存空间,并将空间的首地址返回,如果 size 为 0 ,则返回空指针。
free
1 | void free(void* p); |
释放一段指针指向的内存空间。
calloc
1 | void* calloc(size_t nmemb, size_t size); |
开辟一段连续的内存空间,它的大小为 nmemb * size 字节,并为每一块内存地址赋 0,它实际上依然调用了 malloc 只是在 malloc 的基础上进行了额外的操作。
realloc
函数原型
1 | void* realloc(void* ptr, size_t size); |
重新开辟一段内存空间,将传入的指针 ptr 所指地址的空间调整至 size 大小。如果 size 比原来的空间大,会在另一个地址处开辟一段新的空间,而 ptr 所指的空间被释放;否则,返回的地址仍然是原先的地址,只是合法空间的大小经过了调整。不过即使 realloc 可能返回与 ptr 相同的首地址,但大多数情况下仍然要以下面的方式来使用 realloc 从而避免多次释放的问题。
1 | int main() |
new \ delete
new 和 delete 是 C++ 中新增的动态内存管理的两个运算符,下面是 new 和 delete 的用例。
1 | int main() |
两种方式的区别
下表总结了两种方式之间的区别
特征 | new \ delete | malloc \ free | 解释 |
---|---|---|---|
分配内存的位置 | 自由存储区 | 堆区 | new 和 delete 可以重载,分配到那个存储区取决于具体实现。 |
返回指针的类型 | 完整的类型 | void* | new 开辟出的内存空间不用像 malloc 那样做类型转换,使用起来更方便 |
分配失败的处理 | 抛出异常 | 返回NULL | 抛出异常处理起来更加灵活 |
分配内存的大小 | 由编译器根据类型计算 | 必须显示指定字节数 | |
处理连续空间 | new[] | 必须用户计算数组大小后进行分配 | |
已分配内存的扩充 | 无法直观地处理 | 使用 realloc 完成 | |
是否相互调用 | 可以,取决于重载的实现 | 不可调用 new 和 delete | |
分配内存时内存不足 | 无法通过用户代码处理 | 能够使用 realloc 函数或重新制定分配器 | |
函数重载 | 允许 | 不允许 | |
构造与析构 | 调用 | 不调用 | new 和 delete 为对象开辟/释放空间时会调用对应的构造、析构函数,这在面向对象的程序中极为重要 |
operator new \ placement new \ operator delete
上面我们说的 new
运算符是 new operator
而不是 operator new
这两者的区别在于 new operator
的行为是不可以被改变的,它稳定地完成两个任务:
- 分配内存(调用的是
operator new
) - 调用构造函数初始化
同样 delete operator
也是一样的道理
所谓的重载 new
和 delete
其实重载的是 operator new
和 operator delete
而不是 new operator
和 delete operator
operator new
下面是几个 operator new
的原型(定义在 new
文件中)
1 | // 普通 operator new 版本,如果内存开辟失败,会抛出异常 |
如果要使用第二个版本的 operator new
,需要在使用 new operator
时按下面的方式使用。
1 | int main() |
同样,我们可以自己重写或者重载 operator new
,如下:
1 | void* operator new(size_t size, const char* str) |
程序执行结果为:
operator new hello ..
placement new
在 new
文件中,还定义了一个类型的 operaotr new
,原型如下:
1 | // Default placement versions of operator new. |
这便是 placement new
,它的作用在于在一个已经分配的内存空间上构造一个对象,其语法如下:
1 | int main() |
程序执行结果为:
0x7ffd738daa24
0x7ffd738daa24
257
operator delete
同样,operator delete
也可以像 operator new
那样进行重载操作,如下:
1 | void operator delete(void* ptr) |
执行结果为:
operator delete