文章目录
在C语言中,在 堆栈上申请空间 1的方法就是调用
malloc()
函数,释放空间就调用
free()
函数,而到了C++几乎就看不到他们的身影,为什么C++就不推荐使用这两个函数来申请内存空间了呢,下面我们来详细学习一下在C++里面申请空间的方式。
一,new/delete 和 malloc()/free()
在C/C++中malloc()
和new
的作用是一样的,都是在堆区申请到一块内存。free()
和delete
一样,讲申请到的内存释放掉。但是两者的表现形式和实现方法又有所不同。下面我们重点学习一下。
new
和delete
是C++里面的关键字,它是被C++封装好的,因为C++是在C语言改进后定制的,设计C++的牛人固然要对C语言之前的不足加以改进,而new和delete的就是专门为C++改进的成果。
malloc()
和free()
是C里面的函数,因为C语言是面向过程的,所以对内存的申请,只要调用该函数即可,无需增加其他的东西多此一举,而C++是面向对象的,malloc()
和free()
已经不太能满足需要了,所以设计出了对应的new
和delete
来满足释放对象申请的内存的需要。
二,new和delete的实现原理
前文简绍了new
和delete
是在malloc()
和free()
改进的基础上实现的,那么具体是如何实现的呢,下面来详细分析一下。
首先看下面这段代码:
int main()
{
int* p = new int;
delete p;
return 0;
}
从反汇编可以看到,new
和delete
的使用,他们调用了operator new()
和operator delete()
函数来进行底层操作,我们可以去查阅资料了解这两个函数的功能,下面是两个函数的源代码:
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void* p;
while ((p = malloc(size)) == 0)
{
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);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
void operator new()
和void operator delete()
这两个函数是系统函数,他们的功能实现是借助了malloc()
和free()
,只不过是在实现的过程中进行了其他功能的添加。比如:释放对象的内存时,会调用他的析构函数。所以,new
和delete
的本质调用还是malloc()
和free()
的使用
三,new,delete和malloc(),free()的使用场景
这里建议是:
- new和delete配套使用,malloc()和free()配套使用。
- 一般内置类型new,delete和malloc(),free()可以混搭,因为内置类型没有析构函数,但都建议配套使用,不要混搭。
class A
{
int _a;
int* _ptr;
public:
A()
{
cout << "A()" << endl;
_a = 0;
_ptr = new int;
}
~A()
{
cout << "~A()" << endl;
free(_ptr);
}
};
int main()
{
A* pa = new A;
A* pb = new A;
free(pa);
delete pb;
return 0;
}
如图证明了delete
是会调用对象的析构函数的,而free()
不会,
原因:
- 容易造成内存泄露
- 会有无法预料的错误发生
为什么要搭配使用,其实是由他们各自的实现原理和编译器的执行决定的。比如说下面情况就不行:
内存泄漏
class A
{
int _a;
int* _ptr;
public:
A()
{
_a = 0;
_ptr = new int;
}
~A()
{
free(_ptr);
}
};
int main()
{
A* pa = new A;
free(pa);
return 0;
}
这里
_ptr
指向在堆区的内存未释放。因为free()函数不会去调用对象的析构函数,那么在对象里面申请的空间未释放就造成内存泄漏。
无法预料的错误
class A
{
int _a;
int* _ptr;
public:
A()
{
_a = 0;
_ptr = new int;
}
~A()
{
free(_ptr);
}
};
int main()
{
A* pa = new A[10];
free(pa); // 有可能释放的地址不对,而导致错误
// delete[] pa; // 正确写法
return 0;
}
这个例子在不同平台有不同结果,在vs2013就会报错,而vs2022却不会了。一般报错都是释放的地址错误。
四,new,delete和malloc(),free()的区别
他们的区别可以从三个方面来理解
定义上:
- new,delete是关键字,malloc()和free()是函数
用法上:
- new发生错误报异常,malloc()发生错误返回NULL
- new申请内存空间不需要传大小,malloc()要传大小,还要进行类型转换
- new申请的内存空间可以初始化(用定位new),malloc()的不行
原理上:
- new,delete申请内存时会调用对象的构造和析构函数,malloc()和free()的不会