C++ New 与 delete的多种用法

2021年7月13日 6600点热度 12人点赞 0条评论

C++是一门允许程序员自己管理内存的语言,程序员们可以用使用new和delete自由的在堆空间中申请和释放内存,当然也可以使用从C继承而来的malloc和free。这样的内存管理机制在对内存把控要求极高的应用场景下提供了极大的便利,但是如果程序员对C++的内存管理机制不够熟悉,没有正确的申请和释放内存,造成的后果无疑是灾难性的。那么这里就记录一下new 与delete的多种使用方式。

 

最朴素的使用方式

这是最简单,也是最常用的使用方式

class A;
A *a = new a;
delete a;

首先了解一下这里的new 和 delete做了些什么事情,使用new 时将在堆空间中申请一块内存,然后调用类A的构造函数创建一个类A的对象,本质上是对这块内存进行初始化。使用delete时先调用对象的构造函数,然后释放对象所在的内存。

理一理就是干了这么几件事:

  1. 申请内存
  2. 调用构造函数
  3. 调用析构函数
  4. 释放内存

从这里可以看出来new 与malloc 和 delete与free的区别,new 与 delete在申请和释放内存的同时调用了构造函数和析构函数,而malloc 和 free 只申请和释放内存。

一定要注意的是,new 和 malloc申请的的内存是在堆空间中的,堆中的内存C++是不会去主动释放的,所有用new 和 malloc 申请了内存一定要使用 delete 和 free 释放掉,否则就等着接受被内存泄漏支配的恐惧吧。

动态数组的内存申请

普通的数组的定义

int array[10];

这样定义的变量是存在栈空间中的,并且数组的长度只能是一个常量值,否则编译会不通过。

而使用new可以动态的调整数组的长度,像这样。

int length = 20;
int *array = new int[length];

可以在运行的时候去调整length的值,以调整数组的长度。

这里值得注意的是,释放一个数组需要使用delete[]

Delete[] array;

像是这样调用才行,否则会导致程序抛出异常。

另一个值得注意的地方,就是在定义二维的数组的时候

int n = 42;
auto p1 = new  double[n][5];  // 正确
auto p2 = new  double[5][n];  //错误

这个代码是我在CppReference上看到的,我也试着在编译器了敲了一下,在定义一个动态的二维数组的时候,只有第一个下标可以使用动态的长度。

Operator new 、 operator delete 、placement new

使用new 实例化一个对象时,干了两件事,首先在堆中申请一块内存,然后再调用构造函数构造对象,但是在某些应用场景下需要将这两件事情分开来做,实现类似malloc和free这样的功能,于是有了 operator new 和operator delete(看CppRerence上的介绍,这两个实现应该就是对malloc和free的的封装)。

如果想要只是单纯的想申请一块内存,直接这样写:

void *p = operator new(100); //申请
operator delete(p);  //释放

这里先申请了100个字节大小的内存,然后再释放掉。

那么要在内存中实例化一个对象,这个时候就需要用到placement new,它的原型是这样的:

::(optional) new (placement_params)(optional) type initializer(optional)

整个流程结合起来:

class T;
void *buffer = operator new(sizeof(T));
T *t = new(buffer) T();     //在已有的内存上实例化对象
t->doSomething();
t->~T();    //显示的调用析构函数
operator delete(buffer);

这样就完成了从申请内存-构造对象-析构对象-释放内存,每一步的执行都可以由程序员去自己把握。

这样的代码只有在一些高度依赖内存性能的应用场景才能使用到,如反复的在一块内存中构造和析构对象,这样可以省下申请内存的开销。

这里要注意的一点是析构对象的时候直接显示的调用析构函数可以了。

Notthrow new

使用new申请内存的时候,可能会碰到系统没有足够的内存可以完成当前的内存分配,像是这样

void *pp = operator new(99999999999999999);

这个时候,程序会抛出std::bad_alloc这个异常。

但是有的时候程序员并不希望程序因为异常而停止运行,这个时候可以就需要用到notthrow new。

void *pp = operator new(99999999999999999,std::nothrow);

将代码改成这样,这个时候如果内存分配失败,会返回一个NULL,而不会抛出异常。

这种使用方法可以跟上面的几种new的用法一起使用,都可以阻止new 抛出异常。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论