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/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
92 1
|
3月前
|
C++
C++(十九)new/delete 重载
本文介绍了C++中`operator new/delete`重载的使用方法,并通过示例代码展示了如何自定义内存分配与释放的行为。重载`new`和`delete`可以实现内存的精细控制,而`new[]`和`delete[]`则用于处理数组的内存管理。不当使用可能导致内存泄漏或错误释放。
|
4月前
|
C++ 容器
【C/C++笔记】迭代器
【C/C++笔记】迭代器
30 1
|
4月前
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
74 0
|
4月前
|
存储 程序员 编译器
c++学习笔记08 内存分区、new和delete的用法
C++内存管理的学习笔记08,介绍了内存分区的概念,包括代码区、全局区、堆区和栈区,以及如何在堆区使用`new`和`delete`进行内存分配和释放。
55 0
|
1月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
51 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
106 5