在C语言中,我们想要动态分配内存空间需要使用到malloc,calloc,realloc函数,malloc函数申请空间失败返回指针空值,成功返回空间首地址。
在C++中我们同样有动态进行内存管理的方式,并且与C语言中的内存管理有着一些区别。
new/delete
在C++中我们使用new进行内存的申请,用delete进行内存的释放。他们的使用比malloc和free更加简单方便。
内置类型的内存分配与释放
#include <iostream>
using namespace std;
int main()
{
int* a = new int;
//等同于int* a = (int*)malloc(sizeof(int));
int* b = new int[10];
//等同于int* b = (int*)malloc(sizeof(int) * 10);
int* c = new int(10);
//new还可以进行内置类型的初始化
cout << *c << endl;
delete a;
//等同于free(a);
delete[] b;//对于多个变量的空间释放要用delete[]
//等同于free(b);
delete c;
//等同于free(c);
}
new和malloc一样会在堆上开辟空间同时需要我们手动进行内存的释放,但是new的写法更加简单易于理解同时我们还可以对单个申请的变量进行初始化。
自定义类型的内存分配与释放
#include <iostream>
#include <stdlib.h>
using namespace std;
class Stu
{
public:
Stu()
{
cout << "default building" << endl;
};
Stu(int num, string name):_num(num), _name(name)
{
cout << "custom building" << endl;
}
~Stu()
{
cout << "destroying" << endl;
}
private:
int _num;
string _name;
};
int main()
{
cout << "malloc:" << endl;
Stu* a = (Stu*)malloc(sizeof(Stu));
cout << "new:" << endl;
Stu* b = new Stu(1, "张三");
cout << "malloc:" << endl;
Stu* c = (Stu*)malloc(sizeof(Stu) * 5);
cout << "new:" << endl;
Stu* d = new Stu[5];
cout << "free:" << endl;
free(a);
cout << "delete:" << endl;
delete b;
cout << "free:" << endl;
free(c);
cout << "delete:" << endl;
delete[] d;
}
[root@localhost 内存管理]$ ./New
malloc:
new:
custom building
malloc:
new:
default building
default building
default building
default building
default building
free:
delete:
destroying
free:
delete:
destroying
destroying
destroying
destroying
destroying
以上这段代码我分别使用malloc和new对自定义类型进行内存分配和释放,我们可以发现new不但可以在分配内存的时候手动调用指定的构造函数还会在分配多个对象的空间时自动调用默认构造函数,delete也会自动调用析构函数,而malloc和free却做不到这一点。 因此可以理解为malloc和free分配出来的只不过是一个和类一样大小的空间,并不能称作是一个对象,而new和delete分配出来的才能被成为对象。
new和delete实现原理
new和delete在C++中其实被定义为两个运算符,我们在使用这两个运算符的时候它会在底层调用全局函数operator new和operator delete。
operator new/operator delete
我们首先看下这两个函数在底层的实现源码。
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);
}
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;
}
从源码中能看出的是operator new和operator delete在底层也是利用malloc和free分配内存的,因此可以说new和delete不过是malloc和free的一层封装。
operator new
1.内部通过malloc分配空间
2.如果malloc申请空间失败,设置空间不足的应对措施,则执行,执行之后继续调用malloc再次申请,直到申请成功。
如果没有设置应对措施,直接抛异常(内存空间不足)。
operator delete
封装free,不会抛异常。
因此在某些情况下,我们想要用独特的方式给一个类分配内存空间的时候我们就可以重新重载这两个运算符来达到我们的目的。基于这个原理如果有某些类需要特殊的内存分配方式我们可以对其进行运算符的重载。
实现原理
内置类型
对于内置类型来说new和malloc,delete和free的功能一致,不同的是new[]和delete[]才能分配多个连续的空间。
自定义类型
单个元素空间分配
1、new:
1)首先调用operator new为对象分配空间。
2)调用对象的构造函数对对象进行初始化。
2、delete:
1)调用对象的析构函数进行对象中资源的空间清理。
2)调用operator delete释放对象的空间。
多个元素空间分配
1、new[]:
1)调用operator new[],在operator new[]中调用operator new完成N个对象的空间的分配。
2)调用构造函数N次完成N个对象的初始化。
2、delete[]:
1)调用析构函数N次完成N个对象资源的清理。
2)调用operator delete[],在operator delete[]中调用operator delete完成N个对象的空间的释放
定位new表达式
当我们用malloc或者其他方式分配了一块和某个类一样大小的空间给,并用某个指针去指向这块空间。但是问题在于这块空间并未执行构造函数因此并不能称为对象。因此定位new表达式就是为了帮助我们对一块已经分配好的空间执行构造函数使之成为对象的一个方式。
用法
new (place_address) type(initializer-list)。
place_address为指向某一块空间的指针,type为自定义类型,initializer-list为参数列表。
例
#include <iostream>
#include <stdlib.h>
using namespace std;
class Stu
{
public:
Stu()
{
cout << "default building" << endl;
};
Stu(int num, string name):_num(num), _name(name)
{
cout << "custom building" << endl;
}
~Stu()
{
cout << "destroying" << endl;
}
private:
int _num;
string _name;
};
int main()
{
Stu* p = (Stu*)malloc(sizeof(Stu));
cout << "定位new表达式:" << endl;
new(p) Stu(1,"张三");
delete p;
}
[root@localhost 内存管理]$ ./New
定位new表达式:
custom building
destroying
这样我们就用定位new表达式给已经分配好的空间调用了构造函数。
常见问题
new和malloc的异同
相同
1、new和delete都在堆上进行空间的申请。
2、都需要手动释放空间。
不同
1、malloc和free是函数而new和delete是运算符。
2、new可以在分配空间的时候进行初始化。
3、malloc返回值是void*需要强转,new会直接返回与分配空间类型一样的类型指针。
4、malloc需要手动计算分配空间大小在进行传入,而new只需要类型和元素个数,空间大小会自动计算。
5、new在给自定义类型分配空间的时候会自动调用其构造函数,delete会自动调用其析构函数。
6、malloc申请空间失败会返回NULL,new会抛异常。
7、new和delete是malloc和free的一层封装,因此效率会低一些。