【C++】——面试问我new和delete?后悔没看这篇文章

目录

​编辑

前言

C++内存管理

C++的new 和 delete

new 

delete

new 和 delete 的妙用

new和delete的底层

new和delete的实现原理

扫描二维码关注公众号,回复: 17613286 查看本文章

对内置类型

对自定义类型

 new/delete和malloc/free的区别

内存泄漏

结语


前言

  在C++编程的世界里,内存管理是一项至关重要且基础的任务。有效的内存管理不仅关乎程序的性能、稳定性,更直接影响着软件的可靠性和可维护性。而newdelete作为C++中用于动态内存分配和释放的核心机制,承载着程序员对内存精确掌控的重任。

  从简单的单对象创建到复杂的对象数组管理,从内存的高效利用到避免潜在的内存泄漏风险,newdelete的合理运用贯穿于C++程序开发的始终。深入理解它们的工作原理、使用方法以及可能引发的问题,是每一位C++开发者走向精通的必经之路。本文将深入剖析newdelete的内在机制,探讨它们在不同场景下的应用技巧,并揭示一些常见的陷阱与应对策略,旨在帮助读者更加透彻地掌握这一关键的C++内存管理工具。


C++内存管理

我们知道C++是C语言的升级版,它对C语言虽然是取其精华,去其糟粕

但也只是在原来的基础上进行再一次升级,得出一套自己的更加简单、方便的方式

而内存管理方式,也是C++对C语言升级的一环

C语言的内存管理,基本通过 常用的内存管理函数 malloc函数  和 free函数 来实现

现在简单回顾一下他们的使用

malloc的使用

float *f_ptr = (float*) malloc(sizeof(float));

free的使用

  free(f_ptr);

我们不难发现

使用free其实还好,只需要传递一个指向释放空间的指针

而使用malloc对空间开辟,则需要手动传递 需要空间的大小 , 得到返回值后,还需要对其强制类型转换 

面对简单的类型,我们尚可接收

但一旦类型长了起来,我们就得费一番功夫了

C++的new 和 delete

C语言的内存管理操作本来就很复杂,我们希望能够简单一点

有没有什么简单,不吃操作的方式推荐一下呢?

有的,兄弟,有的

C++为了避免这种繁杂的操作,从而有了自己的一套内存管理方式

内存管理两板斧 new操作符delete操作符

new 

在C++中,new是一个非常重要的操作符,用于在堆(heap)上动态分配内存

重点:new并不是一个函数,它仅仅是一个操作符

它没有函数造型,只有使用方法,用于在堆上一次申请 一个或者 多个空间

基本使用

//申请单一空间
类型名* 对象名 = new 类型名;
//申请多个空间
类型名* 对象数组名 = new 类型名[数组大小];

对比一下 malloc函数 的使用

你就会发现 new 使用起来简单多了

  • 申请一个空间,你只需要在new 后面加上一个 类型名 即可
  • 省去多个空间,你只需要在类型后面 加上你需要多少个这样的空间 

你不需要自己计算并手动传递申请空间的大小,也不需要强制类型转换,这些new都会帮你搞定

delete

delete操作符用于释放通过new操作符动态分配的内存。

基本使用

//释放单个对象
delete 指针;
//指针:指向通过new分配的单个对象的指针。

//释放多个对象
delete[] 指针;
//指向通过new[]分配的对象数组的指针。

注意:delete的释放是需要对标new的,如果new的多个对象,则delete一定要加上[ ],否则会引发不必要的错误,必须严格匹配! 

new 和 delete 的妙用

见过new 和 delete 的使用,你会发现

new 相较于C语言的 malloc 函数,简直不要方便太多

但delete 相较于 free函数,则并没有变化太多

这样一看,仅仅是为了方便,好像并不能成为C++专门创建一套内存管理方式的动机

C++创造new和delete,是为了更好地服务C++的自定义类型

如何服务呢?

来看下面的例子

class MyClass {
public:
    // 构造函数
    MyClass() {
        std::cout << "MyClass的构造函数被调用了" << std::endl;
    }
    // 析构函数
    ~MyClass() {
        std::cout << "MyClass的析构函数被调用了" << std::endl;
    }
};

现有一个自定义类型 MyClass,我们分别用 new 和 delete 对其进行申请空间和释放空间

new  申请空间

// 使用new创建MyClass类型的对象
MyClass* obj = new MyClass();

delete 释放空间

// 手动释放内存,这会调用析构函数
delete obj;

运行结果:

在new的时候,MyClass的构造函数被调用了

在delete的时候,MyClass的析构函数被调用了

所以,new 和 delete 在操作自定义类型空间的时候

  • new会调用自定义类型的 构造函数,完成对自定义类型对象的初始化
  • delete会调用自定义类型的 析构函数,完成对自定义类型对象的销毁

这,就是C++创建new和delete的主要原因,为了提供自定义类型更好的生态,服务于自定义类型

new和delete的底层

我们知道C++的一大特点就是封装,new 和 delete 也是这么来的

它们内部会更加复杂,不像外表这么简单方便

通过汇编,简单对 new 进行查看

可以发现,在用new 向内存申请一个 MyClass类 空间的时候,调用了 operator new 函数 

同样,查看 delete 的汇编,也会发现,delete 调用了 operator delete 函数

注意:有些编译器是看不到调用 operator delete 函数的,这是正常现象

new 和 delete 是用户进行动态内存申请和释放的操作符

operator new 和 operator delete 是系统提供的全局函数

new在底层调用operator new 全局函数来申请空间,delete在底层调用operator delete 全局函数来释放空间

简单看一下 operator new 的实现代码

void* operator new(std::size_t size) {
    //此处省略
    void* ptr = std::malloc(size);
    if (!ptr) throw std::bad_alloc();
    //此处省略
    return ptr;
    
}

operator new 的具体实现,与上面的代码大差不差,内部通过调用 malloc函数 来对空间申请,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户通过该措施继续申请,否则就抛异常

operator delete 也是一样,内部也调用 free函数 来对内存空间进行释放

注意:operator 是运算符重载的标志,我们这里也可以看出来,new和delete 并不是函数,而是操作符

new和delete的实现原理

对内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似

不同的地方是:

new/delete申请和释放的是单个空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛出异常,而malloc会直接返回NULL

对自定义类型

new的原理

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造

delete的原理

  1. 在空间上执行析构函数,完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间

new T[N]的原理

  1. 调用operator new]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数

delete]的原理

  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  2. 调用operator delete]释放空间,实际在operator delete[]中调用operator delete来释放空间

 new/delete和malloc/free的区别

malloc/free和new/delete的共同点是:

都是从堆上申请空间,并且需要用户手动释放。

不同的地方是:

  1.  malloc和free是函数,new和delete是操作符
  2.  malloc申请的空间不会初始化,new可以初始化
  3.  malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即阿
  4.  malloc的返回值为void*,在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5.  malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

内存泄漏

什么是内存泄漏?

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

内存泄漏的危害

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现

最直观的感受,当你的电脑和手机,剩余内存或者容量很少的时候,通常会卡得要死! 


结语

  好了,关于C++ 内存管理的内容我们就讲到这里啦。内存管理就像是盖房子,得把土地(内存)合理地规划好,什么时候盖房子(分配内存),什么时候把房子拆了重新规划(释放内存),都得心里有数。在这个过程中,我们学习了 new 和 delete 这两个大工具,还有 malloc 和 free 也不容小觑。只有把它们用好了,我们的程序才能住得安稳,不会因为内存出了问题而到处出乱子。记住哦,内存管理不是一件轻松的事儿,需要我们多练多总结,慢慢就会越来越熟练啦!希望大家都能在C++ 编程的世界里建造出属于自己的稳固大厦!