new/delete详解(跑路人笔记)<C++初阶>

简介: new/delete详解(跑路人笔记)<C++初阶>

前言

new和delete是C++为了类的使用方便而创造的操作符,我们在使用C++时应该尽量避免使用malloc和free而应该转向使用new和delete.

new/delete

内置类型举例


new/delete类似于malloc/free但是为了搭配我们的C++的新知识也就是类的出现我们在从堆区开辟空间的时候需要调用我们类里的构造函数,而和明显malloc他们并不可能会实现这类又为了兼容C语言C++就增加了new来方便我们自定义类型的从堆开辟空间.


老样子,我们要是讲新知识的时候还是先看看这玩意咋用的


void Test()
{
  // 动态申请一个int类型的空间
  int* ptr1 = new int;
  // 动态申请一个int类型的空间并初始化为10
  int* ptr2 = new int(10);
  // 动态申请10个int类型的空间
  int* ptr3 = new int[3];
    //申请10个int类型的空间并进行初始化,C++11才支持这样写
  int* ptr4 = new int[10]{ 1,2,3,4,5,6,7,8 };
  delete ptr1;
  delete ptr2;
  delete[] ptr3;
  delete[] ptr4;
}
int main()
{
  Test();
  return 0;
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和 delete[]


其中ptr4我们还是单独拿出来说一下,毕竟是C++11才加入的语法


内置类型的newC++11


我们ptr4是C++新加入的初始化写法,其实就和普通的数组初始化类似,而且不能直接给初始化字符串.


image.png


只能通过{}来给数组,感觉意义不大.=.=


内置类型基本就这样了


自定义类型举例

new和delete就是为了我们C++的类而创造的,所以自然在自定义类型有着malloc无法比拟的优点.


直接讲吧,new会在创建类的时候调用构造函数,delete会在销毁类之前调用析构函数,而malloc和free做不到这点


注意: 我们的类里如果有自定义类型,我们的new也会先调用他们的构造函数在delete的时候会调用他们的析构函数.


光说不看看代码还是难理解


先来看看new和delete在自定义类型怎么使用的吧.(类就随便创一个了)


class Test
{
public:
  Test(int a = 1, int b = 2)
  {
  _a = 1;
  _b = 0;
  cout << "Test()" << endl;
  }
  ~Test()
  {
  cout << "~Test()" << endl;
  }
private:
  int _a;
  int _b;
};




image.png


可以看出调用了我们的构造和析构.如果不delete就不会调用, 篇幅问题不展示了.


看一下类里有其他类的情况吧


class Test1
{
public:
  Test1(int a = 1, int b = 2)
  {
  _a = 1;
  _b = 0;
  cout << "Test1()" << endl;
  }
  ~Test1()
  {
  cout << "~Test1()" << endl;
  }
private:
  Test _t1;
  int _a;
  int _b;
};




弄了个Test1里面有Test


来看看结果吧


image.png


调用了,delete也调用了作为成员类的析构函数.


我们看看malloc和free吧.


image.png


就没有调用(其实也很正常毕竟要兼容C语言).


来看看如何使用自定义类型中new的使用吧.


void Test1()
{
  //ptr1的new调用了默认构造函数
  Test* ptr1 = new Test;
  //ptr2调用构造函数的时候传进去了3,3.
  Test* ptr2 = new Test(3, 3);
  //自定义类型数组都调用了默认构造函数
  Test* ptr3 = new Test[10];
  delete ptr1;
  delete ptr2;
  delete ptr3;
}



和普通类型没啥区别.嘿嘿


new失败

new失败和malloc失败时不同new失败会抛异常,如果我们没有接收异常编译器会直接报错.


来看看new失败不接收异常会发生什么吧.


image.png


那我们要如何接收他的异常呢?


int main()
{
  //Test2();
  char* ptr1 = nullptr;
  char* ptr2 = nullptr;
  try
  {
  ptr1 = new char[1024 * 1024 * 1024];
  ptr2 = new char[1024 * 1024 * 1024];
  }
  catch (const exception& e)
  {
  cout << e.what() << endl;
  }
  delete[] ptr1;
  delete[] ptr2;
  return 0;
}


抱歉这部分需要后面的知识才能讲解,但是用法就是这些用法.


这样写我们的编译器就不会报错了.


image.png


operator new与operator delete

注意operator new operator delete是函数名字,不是重载.


我们也没办法重载operator new 或者operator delete .


这两个函数与malloc,free唯一的区别就是operator new失败后是抛异常而malloc是返回空指针.


其实operator new 其实就是使用malloc来实现的不过就是在失败的时候是抛异常了,而不是返回空指针.


然后我们的new又通过operator new 来实现并多加上了调用构造函数.


而我们的delete其实也类似的过程.


总体就是new调用了operator new来实现失败时抛异常然后自己再调用构造函数.


operator new operator delete 其实也是可以重载的,我们可以通过重载类专属 operator new/ operator delete,实现链表节点使用内存池申请和释放内存,提高效率. 不过这是后续的知识了,我们后续会学习的.


现式调用构造函数

代码演示一下就可以了


class Test
{
public:
  Test(int a = 1, int b = 2)
  {
  _a = 1;
  _b = 0;
  cout << "Test()" << endl;
  }
  ~Test()
  {
  cout << "~Test()" << endl;
  }
private:
  int _a;
  int _b;
};
int main()
{
  Test* p = (Test*)operator new (sizeof(Test));
  new(p)Test(1, 2);//现式调用构造函数
//  new(指针)类名(要传入的值)
  return 0;
}


现式调用构造函数形式如代码上.


或看下:


new(指向类的指针)+类名(初始化的值);


new和delete的实现原理

new的实现原理


调用operator new函数申请空间

调用构造函数来完成类的初始化.

delete的实现原理


调用析构函数完成类里的空间释放或销毁


调用operator delete函数释放开辟类的空间


new[]的实现原理


调用operator new[]

operator new[]实际调用多个operator new来完成N个空间的开辟

调用类的构造函数

delete[]的实现原理


在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

malloc/free和new/delete的区别小结

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。


不同的地方是:


malloc和free是函数,new和delete是操作符

malloc申请的空间不会初始化,new可以初始化

malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可

malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型

malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常

申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间 后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

结尾

觉得好的话记得点个赞.


相关文章
|
16天前
|
C++
C++(十九)new/delete 重载
本文介绍了C++中`operator new/delete`重载的使用方法,并通过示例代码展示了如何自定义内存分配与释放的行为。重载`new`和`delete`可以实现内存的精细控制,而`new[]`和`delete[]`则用于处理数组的内存管理。不当使用可能导致内存泄漏或错误释放。
|
30天前
|
C++ 容器
【C/C++笔记】迭代器
【C/C++笔记】迭代器
15 1
|
30天前
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
52 0
|
1月前
|
存储 程序员 编译器
c++学习笔记08 内存分区、new和delete的用法
C++内存管理的学习笔记08,介绍了内存分区的概念,包括代码区、全局区、堆区和栈区,以及如何在堆区使用`new`和`delete`进行内存分配和释放。
38 0
|
2月前
|
NoSQL 编译器 Redis
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响
|
2月前
|
C++ Windows
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
在Windows上使用Visual Studio 2022进行FFmpeg和SDL2集成开发,首先安装FFmpeg至E:\msys64\usr\local\ffmpeg,然后新建C++控制台项目。在项目属性中,添加FFmpeg和SDL2的头文件及库文件目录。接着配置链接器的附加依赖项,包括多个FFmpeg及SDL2的lib文件。在代码中引入FFmpeg的`av_log`函数输出"Hello World",编译并运行,若看到"Hello World",即表示集成成功。详细步骤可参考《FFmpeg开发实战:从零基础到短视频上线》。
68 0
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
|
2月前
|
存储 C语言 C++
【C/C++】动态内存管理( C++:new,delete)
C++的`new`和`delete`用于动态内存管理,分配和释放内存。`new`分配内存并调用构造函数,`delete`释放内存并调用析构函数。`new[]`和`delete[]`分别用于数组分配和释放。不正确匹配可能导致内存泄漏。内置类型分配时不初始化,自定义类型则调用构造/析构。`operator new`和`operator delete`是系统底层的内存管理函数,封装了`malloc`和`free`。定位`new`允许在已分配内存上构造对象,常用于内存池。智能指针等现代C++特性能进一步帮助管理内存。
|
2月前
|
NoSQL Redis C++
c++开发redis module问题之避免多个C++模块之间因重载operator new而产生的冲突,如何解决
c++开发redis module问题之避免多个C++模块之间因重载operator new而产生的冲突,如何解决
|
2月前
|
C++
C++基础知识(二:引用和new delete)
引用是C++中的一种复合类型,它是某个已存在变量的别名,也就是说引用不是独立的实体,它只是为已存在的变量取了一个新名字。一旦引用被初始化为某个变量,就不能改变引用到另一个变量。引用的主要用途包括函数参数传递、操作符重载等,它可以避免复制大对象的开销,并且使得代码更加直观易读。
|
2天前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
42 30