new出对象“原理的深层解密

简介: new出对象“原理的深层解密

前言

讲解C++中有关new的知识,与malloc进行对比,以及深入探索new的实现原理.

一、malloc和new的使用

在C语言阶段,我们习惯使用malloc向内存申请空间,但是在C++阶段,我们习惯用new在动态内存中创建对象,为什么呢?

1.1 new创建内置类型(int等)

在创建内置类型时,new只是不需要进行强转和计算内置类型的大小,看起来更加简洁,方便.

  //malloc申请内置类型
  int* p1 = (int*)malloc(sizeof(int));
  free(p1);
  //new对比
  int* ptr1 = new int;
  delete ptr1;

1.2 new创建数组

new+ 对象的类型 +[个数] +(初始化的值)


new+ 对象的类型 +[个数] +{num1,num2,…}


需要注意的是,连续的多个空间须使用new[]与delete[]搭配

  //申请数组
  int* p3 = (int*)malloc(sizeof(int) * 10);
  //赋值
  for (int i = 0; i < 10; i++){
    p3[i] = i;
  }
  //打印
  for (int i = 0; i < 10; i++){
    cout << p3[i] << " ";
  }
  cout << endl;
  // new创建数组
  int* ptr3 = new int[10]{0,1,2,3,4,5,6,7,8,9};
  for (int i = 0; i < 10; i++){
    cout << ptr3[i] << " ";
  }
  //释放
  free(p3);
  delete[] ptr3;

1.3 创建对象

如何使用new进行创建对象?

#include <iostream>
using namespace std;
#include<stdlib.h>
class Date
{
public:
  Date()
    :_year(2020)
    ,_month(6)
    ,_day(6)
  {
    cout << "A()" << endl;
  }
  void print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
  ~Date()
  {
    cout << "~A()" << endl;
    free(_a);
  }
private:
  int _year;
  int _month;
  int _day;
  int* _a=nullptr;
};
int main()
{
  //malloc出对象
  Date* d1 = (Date*)malloc(sizeof(Date));
  d1->print();
  free(d1);
  //new出对象
  Date* d2 = new Date;
  d2->print();
  delete d2;
  return 0;
}

运行结果:

-842150451–842150451–842150451
A()
2020-6-6
~A()

通过上段代码我们发现,malloc只是进行开空间的操作,对象并没有得到初始化操作.

new则是在开空间的同时,会调用对象的构造函数,将对象进行初始化.

free只是进行简单的释放申请的空间,如果对象中存在动态申请的成员,则无法进行释放.

delete会在释放申请的对象空间的同时,调用对象的析构函数,彻底的完成空间的清理工作.

1.4 异常处理

对于malloc函数,当malloc申请内存空间失败的时候,会返回一个NULL指针.

我们通常通过判断返回值是否为NULL来判断是否申请成功.

  int* a = (int*)malloc(10000* sizeof(int));
  if (a == NULL)
  {
    perror("malloc a fail");//申请失败时,打印错误信息
    return 0;
  }

new失败不会返回NULL,而是通过抛出异常.

在C++中,可以使用try-catch语句来捕获new操作符抛出的异常。new操作符在内存分配过程中如果失败,会抛出一个bad_alloc异常。


示例代码:

try {
    int* myArray = new int[10000]; // 分配一个包含10000个整数的数组
    // ...
    delete[] myArray; 
}
catch (const std::bad_alloc& e) {
    // 处理内存分配失败的异常
    std::cout << "内存分配失败: " << e.what() << std::endl;
}

在上述代码中,new操作符用于分配一个包含10000个整数的数组。如果内存分配失败,将抛出一个bad_alloc异常。catch语句块接收这个异常,并执行相应的处理代码。在这个示例中,异常被捕获后会打印一条错误消息。

需要注意的是,catch语句块中的参数类型应为const std::bad_alloc&,因为bad_alloc是标准异常类,它派生自std::exception,通常以常量引用的形式传递给异常处理代码。

二、malloc和new的区别:(面试热门)

在C++中,malloc和new都用于在堆上分配内存,但有一些重要的区别。

1.语法和类型安全性:malloc和free是函数,new和delete是操作符

 (1)malloc是C语言中的函数,malloc需要指定要分配的内存大小,并返回一个指向未初始化内存块的指针。

 (2)new是C++中的运算符。new可以直接在创建对象时进行初始化,并返回一个指向已经构造的对象的指针。new操作符会执行类型检查,确保分配的内存与对象类型匹配。

2.构造函数和析构函数调用:

 (1)使用new分配内存时,会自动调用对象的构造函数进行初始化。

 (2)使用malloc分配内存时,不会调用对象的构造函数,需要手动调用构造函数初始化对象。

 (3)同样,使用delete释放new分配的内存时,会自动调用析构函数进行清理工作。而使用free释放malloc分配的内存时,不会自动调用析构函数,需要手动执行清理操作。

3.内存大小计算:

 (1)使用malloc分配内存时,需要显式指定要分配的内存块的大小,以字节为单位。

 (2)使用new分配单个对象时,编译器会自动计算所需的内存大小,以对象的类型为基础。对于数组对象,需要使用new[]和delete[],同样会自动计算所需的内存。

4.异常处理:new在分配内存失败时,会抛出std::bad_alloc异常,而malloc在分配内存失败时,返回NULL指针。

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

总的来说,new相对于malloc提供了更高级的、更安全的内存分配方式,能够自动调用构造函数和析构函数,执行类型检查,并提供异常处理。因此,在C++中,推荐使用new和delete来进行动态内存分配和释放。如果你需要使用C语言的库或与C代码进行交互,可以使用malloc和free。

三、new和delete的深层解密

3.1 解密实现原理

学到这里,我们知道new会代用构造函数,还会抛出异常,那它究竟是怎么实现的呢?

27ee2d5aa5a944e9933e9b287c9b7c4e.png

operator new的实现

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
void *p;
while ((p = malloc(size)) == 0)//通过mallo开空间
 if (_callnewh(size) == 0)
     {
         // report no memory
         // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
         static const std::bad_alloc nomem;
         _RAISE(nomem);
     }
return (p);
}

e1dd16f3a61d40928e01f4f177843f4e.png

看不懂没关系,只需要知道operator delete调用了free函数即可

void operator delete(void *pUserData)
{
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);  /* block other threads */
     __TRY
         /* get a pointer to memory block header */
         pHead = pHdr(pUserData);
          /* verify block type */
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );//调用了free函数
     __FINALLY
         _munlock(_HEAP_LOCK);  /* release other threads */
     __END_TRY_FINALLY
     return;
}

free的实现就是一个宏定义_free_dbg(p, _NORMAL_BLOCK)

#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

我们可以直接调用operator new 和operator delete函数.

void test1()
{
  A* a1 = (A*)operator new (sizeof(A));
  A* a2 = (A*)malloc (sizeof(A));
  operator delete(a1);
  free(a2);
}
int main()
{
  test1();
  return 0;
}

发现operator new 的使用和malloc没什么区别,

只是一个抛异常.

一个返回NULL.

image.png

3.2 通过汇编指令验证

void test1()
{
  A* a1 = new A;
  delete a1;
}

通过调试窗口的反汇编窗口,我们查看A* a1 = new A;对应的汇编指令:

d44be036485043e48f046ccd5f19aa2f.png

会发现,new操作符果然是调用operator new +构造函数.


查看delete操作符,由于vs编译器进行了再封装,我们需要进到下面这条指令里面去看:

f6e044ee6ebb47d08fb2987a3af069cd.png

不难发现,delete操作符=调用析构函数+调用operator delete函数

0e84b09a36a749d1ac1c81595a060d3a.png

好的,本篇有关new操作符和delete操作符的相关知识就讲到这里了,希望对大家有所帮助.


如果觉得文章有帮助的话,可以来个一键三连吗?

ae5cbfb2d20e49f7ba610e50010075b7.gif

目录
相关文章
|
2月前
|
机器学习/深度学习 人工智能 算法
模型无关的局部解释(LIME)技术原理解析及多领域应用实践
在当前数据驱动的商业环境中,人工智能(AI)和机器学习(ML)已成为各行业决策的关键工具,但随之而来的是“黑盒”问题:模型内部机制难以理解,引发信任缺失、监管合规难题及伦理考量。LIME(局部可解释模型无关解释)应运而生,通过解析复杂模型的个别预测,提供清晰、可解释的结果。LIME由华盛顿大学的研究者于2016年提出,旨在解决AI模型的透明度问题。它具有模型无关性、直观解释和局部保真度等优点,在金融、医疗等领域广泛应用。LIME不仅帮助企业提升决策透明度,还促进了模型优化和监管合规,是实现可解释AI的重要工具。
116 9
|
7月前
|
存储 程序员 编译器
在C++语言中局部对象
在C++语言中局部对象
51 0
|
5天前
|
机器学习/深度学习 人工智能 自然语言处理
LEC: 基于Transformer中间层隐藏状态的高效特征提取与内容安全分类方法
通过利用Transformer中间层的隐藏状态,研究提出了层增强分类(LEC)技术,该技术能够以极少的训练样本和参数实现高效的内容安全和提示注入攻击分类,显著提升了模型的性能,并验证了其跨架构和领域的泛化能力。
32 11
LEC: 基于Transformer中间层隐藏状态的高效特征提取与内容安全分类方法
|
26天前
|
机器学习/深度学习 运维 监控
基于特征子空间的高维异常检测:一种高效且可解释的方法
本文探讨了一种替代传统单一检测器的方法,通过构建多个专注于特征子集(子空间)的检测器系统,来提高异常检测的准确性和效率。文章详细介绍了子空间方法在处理高维数据时的优势,包括缓解维度灾难、提高异常检测的可解释性和计算效率。同时,文中还讨论了子空间的选择策略,如基于领域知识、相关性、随机选择等,并介绍了PyOD工具包中实现子空间异常检测的具体方法。通过这些技术,异常检测系统能够更有效地识别数据中的异常记录,尤其是在特征数量众多的情况下。
44 9
基于特征子空间的高维异常检测:一种高效且可解释的方法
|
5月前
|
机器学习/深度学习 移动开发 自然语言处理
【YOLOv8改进 - 注意力机制】ContextAggregation : 上下文聚合模块,捕捉局部和全局上下文,增强特征表示
【YOLOv8改进 - 注意力机制】ContextAggregation : 上下文聚合模块,捕捉局部和全局上下文,增强特征表示
|
7月前
|
机器学习/深度学习 自然语言处理 监控
卷积神经网络的原理、结构和应用
【4月更文挑战第7天】
138 0
卷积神经网络的原理、结构和应用
|
7月前
|
机器学习/深度学习 算法 PyTorch
卷积神经网络的结构组成与解释(详细介绍)
卷积神经网络的结构组成与解释(详细介绍)
330 0
|
7月前
|
机器学习/深度学习 自然语言处理 算法
深度解析预训练权重的本质和作用
深度解析预训练权重的本质和作用
294 1
|
机器学习/深度学习 算法 决策智能
最大熵图像复原方法原理(附完整代码)
最大熵图像复原方法原理(附完整代码)
237 0
|
7月前
|
C++
温故知新-局部对象
温故知新-局部对象
44 0