new和delete的理解
Qingyh Lv3

malloc/free和new/delete的区别

  • 属性区别
    malloc和free,是c的库函数,通常需要包含头文件;而new和delete,是C++中的运算符,可以重载

  • 使用区别
    malloc申请内存空间时需要显式填入申请内存的大小。malloc内存分配成功时返回void * ,需要通过强制类型转换,将void*指针转换成我们需要的类型。malloc分配内存失败时返回NULL,我们可以通过返回值判断内存是否分配成功。
    new会根据new的类型来分配内存,所以无需显式填入申请的内存大小。new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,所以new是符合类型安全性的操作符,在C++程序中使用new会比malloc安全可靠。new内存分配失败时,不会返回NULL,而是抛出std::bad_alloc异常。

  • 内存分配区别
    malloc申请的内存是在堆空间。堆是操作系统分配给进程的一块特殊内存区域,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,调用free()归还内存。
    new分配的内存空间是在自由存储区。自由存储区是C++中动态分配和释放对象的一个概念,通过new分配的内存区域可以称为自由存储区,通过delete释放归还内存。自由存储区可以是堆,也可以是全局静态存储区,具体是在哪个区,要看new的实现以及C++编译器默认new申请的内存是在哪里。大多C++编译器默认使用堆来实现自由存储,new和delete内部默认使用malloc和free的方式来被实现。

  • 是否可以重载:new和delete在C++中是运算符,所以是可以重载的;而malloc和free是C里的库函数,无法对其进行重载。

总结来说,new和malloc都是动态内存分配的手段,但new提供了类型安全和构造/析构的自动化,而malloc则提供了更底层的内存分配方式,需要手动管理构造和析构。在C++中,推荐使用new来分配对象,以保持类型安全和自动化的资源管理。

进一步可参考:
malloc free和new delete有什么区别问答?

代码示例

下面是使用 malloc/freenew/delete的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int *p=(int *)malloc(sizeof(int));
if (p==nullptr)
{
return -1;
}
*p=20;//需要单独进行初始化
free(p);

//等效的new和delete操作为
try
{
int *p1=new int (20);
}
catch(const bad_alloc &e)//捕捉错误
{
}
delete p1;


//在堆上开辟数组操作
int *q=(int *)malloc(sizeof(int)*20);
if (q==nullptr)
{
return -1;
}
free(q);

int *q1=new int[20];//仅负责开辟内存
int *q1=new int[20]();//这种会初始化开辟的内存为零,不能传入其他值
delete []q1;

new的种类

分为以下四种:

  • 基本运算符用法
  • 不抛出异常运算符方法
  • 在堆上创建常量
  • 定位new用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 1.基本new运算符用法:在堆上分配内存并初始化
int *p1=new int(20);
// 分配一个int大小的内存空间,并用20初始化该内存
// 如果分配失败会抛出std::bad_alloc异常

// 2.nothrow版本的new:分配失败时不抛出异常
int *p2=new (nothrow) int;//不抛出异常
// 分配一个int大小的未初始化内存空间
// 如果分配失败会返回nullptr而不是抛出异常
// 使用前需要检查指针是否为null:if(p2 != nullptr)...

// 3.在堆上创建常量对象
const int *p3=new const int(40); //堆上开辟常量
// 分配一个const int内存空间并初始化为40
// 该指针指向的值不可修改:*p3 = 50; 否则会编译错误


// 定位new(placement new)用法:在指定内存位置构造对象
int data=0; // 先在栈上定义一个变量
int *p4=new (&data) int(50);
// 在data变量的地址上构造一个int对象并初始化为50
// 不会分配新内存,而是重用现有内存位置
cout<<"data:"<<data<<endl;

// 使用后需要手动释放内存(定位new除外)
delete p1; // 释放p1指向的内存
delete p2; // 释放p2指向的内存
delete p3; // 释放p3指向的内存
// p4不需要delete,因为它指向的是栈内存data

new/delete[] 与 new[]/delete 不可混用的原因

首先重载newdelete 以及new []delete []运算符,实现自定义内存管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//这里是全局运算符重载,适用于所有类的内存分配和释放
//先开辟内存空间,再调用对象构造函数
void* operator new(size_t size)
{
void* p = malloc(size); //使用malloc分配指定大小内存
if (p == nullptr) //检查分配是否成功
{
throw bad_alloc(); //抛出异常
}
cout << "operator new addr:" << p << endl;
return p; //返回内存指针
}

//先析构对象,再清除内存空间
void operator delete(void* ptr)
{
free(ptr);
cout << "operator delete addr:" << ptr << endl;
}

/*数组版本的重载*/
//new[]操作符的实现,用于分配对象数组
//与new类似,但会额外处理数组相关的信息
void* operator new[](size_t size)
{
void* p = malloc(size);
if (p == nullptr)
{
throw bad_alloc();
}
cout << "operator new[] addr:" << p << endl;
return p;
}

//与delete类似,但会先调用数组中每个元素的析构函数(如果是对象)
void operator delete[](void* ptr)
{
cout << "operator delete addr:" << ptr << endl;
free(ptr);
}

new[]和delete p混用

随后定义一个Test类,用以分析new和delete。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//自定义的Test类,
class Test
{
public:
Test() { cout << "Test" << endl; }
~Test() { cout << "~Test" << endl; }
private :
int x;
};
int main()
{
Test* p = new Test[5];
cout << "Test类的大小:" << sizeof(Test) << endl;
cout << "首个对象的地址:" << p << endl;
delete p; //这是错误的
}

image
根据实验结果,可以发现开辟的内存起始地址和返回的首元素对象地址相差8个字节,这是因为当使用new[]的时候,编译器在开辟内存空间的时候会专门开辟8个字节(4或者8字节,根据编译器不同)用以存放数组的个数。所以这里开辟的内存空间应该是8+5*4=28个字节。
image
而如果在delete的时候,选择delete p,编译器认为这只是一个简单的对象,只是把数组中首元素删除了,而不是从p-8的位置开始释放,从而造成了内存泄露。
image

new和delete []混用

1
2
3
4
5
int main()
{
Test* p = new Test();
delete []p;
}

创建new的时候是一个普通变量,但是在delete删除的时候告诉编译器这是一个数组。
那么编译器就会从p-8(非法内存)的位置读取虚构的数组长度值,并从p-8的位置开始释放内存。但是p-8的位置不是当前p所指向的合法空间,这是越界访问,程序出错。

总结

对于内置类型,编译器允许混用new 和delete[],new[] 和delete。
new []和delete混用示例
image
new 和delete[]混用示例
image
但强烈建议避免混用!!!

对于自定义类型(存在析构函数的情况),为正确调用析构函数,在创建对象数组时会额外分配4或8字节用于记录对象数量,此时混用就会报错了。

$share-item-width = 1.8rem .post-share-container { flex-shrink 0 .share-list-wrap { display flex justify-content flex-end .share-item { width $share-item-width height $share-item-width margin-left 0.5rem padding 0.4rem border-style solid border-width 0.1rem border-radius 50% cursor pointer transition-t("background", "0", "0.3", "ease") i { color inherit font-size 1rem } &.qq { color var(--keep-primary-color) border-color var(--keep-primary-color) &:hover { color var(--background-color-1) background var(--keep-primary-color) } } &.wechat { color var(--keep-success-color) border-color var(--keep-success-color) img { filter brightness(1) !important &[lazyload] { &::before { background #fff !important } } } &:hover { color var(--background-color-1) background var(--keep-success-color) } } &.weibo { color var(--keep-danger-color) border-color var(--keep-danger-color) &:hover { color var(--background-color-1) background var(--keep-danger-color) } } } } }
if (hexo-config('comment') && hexo-config('comment.enable') == true && hexo-config('comment.use')) { if (hexo-config('comment.use') == "valine") { @import "./valine.styl" } else if (hexo-config('comment.use') == "gitalk") { @import "./gitalk.styl" } else if (hexo-config('comment.use') == "twikoo") { @import "./twikoo.styl" } else if (hexo-config('comment.use') == "waline") { @import "./waline.styl" } } .comments-container { display inline-block width 100% margin-top var(--component-gap) .comment-area-title { width 100% color var(--text-color-3) font-size 1.38rem line-height 2 i { color var(--text-color-3) } +keep-tablet() { font-size 1.2rem } } .configuration-items-error-tip { display flex align-items center margin-top 1rem color var(--text-color-3) font-size 1rem i { margin-right 0.3rem color var(--text-color-3) font-size 1.2rem } } .comment-plugin-fail { display none flex-direction column align-items center justify-content space-around width 100% padding 2rem .fail-tip { color var(--text-color-3) font-size 1.1rem } .reload { margin-top 1rem } } .comment-plugin-loading { flex-direction column padding 1rem color var(--text-color-3) .loading-icon { color var(--text-color-4) font-size 2rem } .load-tip { margin-top 1rem color var(--text-color-4) font-size 1.1rem } } }
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 42.9k
$li-margin-bottom = 0.8rem $post-tool-button-width = 2.5rem .post-tools-container { padding-top var(--component-gap) .post-tools-list { li { margin-bottom $li-margin-bottom &:last-child { margin-bottom 0 } } li.tools-item { position relative box-sizing border-box width $post-tool-button-width height $post-tool-button-width color var(--text-color-3) font-size 1.2rem background var(--background-color-1) border-radius 50% box-shadow 2px 2px 5px var(--shadow-color) cursor pointer &:hover { box-shadow 2px 2px 8px var(--shadow-hover-color) } i { color var(--text-color-3) } &:hover { color var(--background-color-1) background var(--primary-color) i { color var(--background-color-1) !important } } &.toggle-show-toc { display none } &.go-to-comments { .post-comments-count { position absolute top 0 right -1rem display none align-items center justify-content center box-sizing border-box min-width 1.1rem height 1.1rem padding 0 0.2rem color var(--badge-color) font-size 12px background var(--badge-background-color) border-radius 0.4rem +keep-tablet() { display none !important } } } } li.status-item { width $post-tool-button-width height $post-tool-button-width color var(--text-color-3) font-size 1.6rem cursor pointer &.post-lock { cursor default .fa-lock-open { display none color var(--keep-success-color) } &.decrypt { cursor pointer .fa-lock-open { display block } .fa-lock { display none } } } } } }