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/free和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
| int *p=(int *)malloc(sizeof(int)); if (p==nullptr) { return -1; } *p=20; free(p);
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
| int *p1=new int(20);
int *p2=new (nothrow) int;
const int *p3=new const int(40);
int data=0; int *p4=new (&data) int(50);
cout<<"data:"<<data<<endl;
delete p1; delete p2; delete p3;
|
new/delete[] 与 new[]/delete 不可混用的原因
首先重载new和delete 以及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); 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; }
void* operator new[](size_t size) { void* p = malloc(size); if (p == nullptr) { throw bad_alloc(); } cout << "operator new[] addr:" << p << endl; return p; }
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
| 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
}
}
}