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在释放空间前会调用析构函数完成空间中资源的清理

结尾

觉得好的话记得点个赞.


相关文章
|
2月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
120 26
|
11月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
155 4
|
11月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
199 3
|
11月前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
212 2
|
11月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
196 1
C++(十九)new/delete 重载
本文介绍了C++中`operator new/delete`重载的使用方法,并通过示例代码展示了如何自定义内存分配与释放的行为。重载`new`和`delete`可以实现内存的精细控制,而`new[]`和`delete[]`则用于处理数组的内存管理。不当使用可能导致内存泄漏或错误释放。
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
145 0
|
7月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
3月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
78 0
|
3月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
158 0