🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解C++中的有关内存管理知识,如何new出对象?
金句分享:
✨如果事与愿违,请相信另有安排.✨
前言
讲解C++
中有关new
的知识,与malloc
进行对比,以及深入探索new
的实现原理.
目录
- 前言
- 一、malloc和new的使用
- 1.1 new创建内置类型(int等)
- 1.2 new创建数组
- 1.3 创建对象
- 1.4 异常处理
- 二、malloc和new的区别:(面试热门)
- 三、new和delete的深层解密
- 3.1 解密实现原理
- 3.2 通过汇编指令验证
一、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
都用于在堆上分配内存,但有一些重要的区别。
-
语法和类型安全性:
malloc
和free
是函数,new
和delete
是操作符
(1)malloc
是C语言中的函数,malloc
需要指定要分配的内存大小,并返回一个指向未初始化内存块的指针。
(2)new
是C++
中的运算符。new
可以直接在创建对象时进行初始化,并返回一个指向已经构造的对象的指针。new
操作符会执行类型检查,确保分配的内存与对象类型匹配。 -
构造函数和析构函数调用:
(1)使用new
分配内存时,会自动调用对象的构造函数进行初始化。
(2)使用malloc
分配内存时,不会调用对象的构造函数,需要手动调用构造函数初始化对象。
(3)同样,使用delete
释放new
分配的内存时,会自动调用析构函数进行清理工作。而使用free
释放malloc
分配的内存时,不会自动调用析构函数,需要手动执行清理操作。 -
内存大小计算:
(1)使用malloc
分配内存时,需要显式指定要分配的内存块的大小,以字节为单位。
(2)使用new
分配单个对象时,编译器会自动计算所需的内存大小,以对象的类型为基础。对于数组对象,需要使用new[]
和delete[]
,同样会自动计算所需的内存。 -
异常处理:
new
在分配内存失败时,会抛出std::bad_alloc
异常,而malloc
在分配内存失败时,返回NULL
指针。 -
malloc
的返回值为void*
, 在使用时必须强转,new
不需要,因为new
后跟的是空间的类型
总的来说,new
相对于malloc
提供了更高级的、更安全的内存分配方式,能够自动调用构造函数和析构函数,执行类型检查,并提供异常处理。因此,在C++
中,推荐使用new
和delete
来进行动态内存分配和释放。如果你需要使用C语言的库或与C代码进行交互,可以使用malloc
和free
。
三、new和delete的深层解密
3.1 解密实现原理
学到这里,我们知道new会代用构造函数,还会抛出异常,那它究竟是怎么实现的呢?
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);
}
看不懂没关系,只需要知道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
.
3.2 通过汇编指令验证
void test1()
{
A* a1 = new A;
delete a1;
}
通过调试窗口的反汇编窗口,我们查看A* a1 = new A;
对应的汇编指令:
会发现,new
操作符果然是调用operator new
+构造函数.
查看delete
操作符,由于vs
编译器进行了再封装,我们需要进到下面这条指令里面去看:
不难发现,delete
操作符=调用析构函数+调用operator delete
函数
好的,本篇有关new
操作符和delete
操作符的相关知识就讲到这里了,希望对大家有所帮助.
如果觉得文章有帮助的话,可以来个一键三连吗?