在C语言当中, 动态内存的管理方式分为 malloc / calloc / realloc 和 free
-
malloc函数可以在堆上申请指定字节的内存空间, 申请成功返回申请到的空间的首地址, 如果申请失败会返回NULL.
void* malloc(size_t size);
注意malloc申请到的空间是没有经过初始化的 -
calloc函数与malloc的功能相似, 但是calloc申请到的空间是经过初始化的, 初始化内容为0.
void* malloc(size_t, num, size_t size);//申请num个字节大小为size的空间
-
realloc函数
void* realloc (void* ptr, size_t size);
realloc函数会改变ptr所指向空间的内存大小, 其中ptr必须是指向堆内存空间的指针, 也就是由malloc/calloc/realloc函数所分配空间的指针. 如果size小于或等于之前ptr所指向空间的大小, 就会保持原来状态不变, 如果size大于之前ptr所指向空间的大小, 会重新开辟一块大小为size的内存空间, 并将原来所指向空间中的内容复制到新的空间上. realloc函数所申请的空间也是没有经过初始化的.
C语言内存管理方式在C++当中仍然可以继续使用, 但有些地方用起来并不方便, 比如开辟自定义类型的空间, 因此在C++当中又提出了自己的诶存管理方式, 通过new和delete操作符进行动态内存管理.
new / delete对于内置类型的操作
内置类型就是 int, char, float, double等
void test()
{
//动态申请一个int类型的空间
int* pi = new int;//申请一个4字节整型空间
//动态申请一个int类型的空间并初始化
int* pi2 = new int(2);//申请一个4字节整型空间并赋值为2
//动态申请int类型的数组
int* pi3 = new int[5];//申请了一个长度为5的整型数组
delete pi;
delete pi2;
delete[] pi3;
}
注意:
-
注意申请空间时 () 和 [] 的区别, ()是将申请到的空间初始化, 而 [] 是申请数组时用的;
-
对应的释放空间时, 释放连续空间(即数组)时记得要用到 delete[].
-
int* pi4 = new int[5](1);//这样写是不可以的
其实看到这里, 我们就会想, 在C语言当中已经有了 malloc / calloc / realloc / free这些函数, 那为什么在C++当中又要出现 new / delete, new [] / delete[]?
首先, 针对内置类型来说, 无论使用 new 还是 malloc 等其实都是一样的.
对于自定义类型来说, 就会不一样了. 比如下面的例子:
自定义类型 A
class A
{
public:
A(int x = 10) :
_val(x)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _val;
};
用malloc申请空间
void test2()
{
A* pA_C = (A*)malloc(sizeof(A));
cout << pA_C << endl;
cout << pA_C->getVal() << endl;
free(pA_C);
}
输出结果:
空间申请成功, 类成员无法初始化打印随机值.
接下来使用 new / delete
void test1()
{
A* pA_CPP = new A;//申请单个A类型的对象
cout << pA_CPP << endl;
cout << pA_CPP->getVal() << endl;
delete pA_CPP;//释放掉这片空间
}
输出结果:
可以很清楚的看到, new在针对自定义类型申请空间的时候, 会自动调用构造函数, 构造函数又帮我们完成了对类成员_val的初始化, 而delete会自动调用析构函数完成资源的清理工作.
我们可以进一步验证一下, new / delete 针对自定义类型的操作, 接下来我们申请一个A类型的对象数组
// new / delete
A* pA_CPP_ARR = new A[10];
delete[] pA_CPP_ARR;
调用10次构造函数, 10次析构函数
// malloc
A* pA_C_ARR = (A*)malloc(sizeof(A)* 10);
free(pA_C_ARR);
malloc只开辟空间.
结论: 建议在C++当中使用 new / delete
operator new和operator delete函数
看到operator我们一定会想到运算符重载, 某种角度来说这样理解也可以, 但是这里我们要尽量将其看做一个整体, 看做函数来理解它.
同样, 先给一个自定义类型
class A
{
public:
A(int x = 10) :
_val(x)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int getVal()
{
return _val;
}
private:
int _val;
};
接下来申请空间
void test1()
{
A* ptr1 = (A*)malloc(sizeof(A));
A* ptr2 = new A(1);
A* ptr3 = (A*)operator new(sizeof(A));
free(ptr1);
delete ptr2;
operator delete(ptr3);
}
执行结果
均申请空间成功, 通过上面的讲解我们知道, new / delete 会调用构造和析构, 那么operator new / operator delete 又是干什么用的呢?它与malloc / free的区别在哪里呢?
-
首先operator new / operator delete的使用与 malloc / free相同
void* operator new(size_t size); operator delete(void* ptr);
-
举个例子来看operator new与malloc的区别
//用malloc去申请空间 size_t size = 2; void* ptr1 = malloc(size * 1024 * 1024 * 1024); cout << ptr1 << endl;//申请失败, 返回NULL(0)
//用operator new去申请空间
void* ptr2 = operator new(size * 1024 * 1024 * 1024);
cout << ptr2 << endl;
程序会崩掉. 因为operator new对比malloc, operator new申请空间失败会抛异常(面向对象的错误处理方式), 必须跳到跳到catch的地方, 所以代码要修改为下面这样
try
{
void* ptr2 = operator new(size * 1024 * 1024 * 1024);
cout << ptr2 << endl;
}
catch (exception& e)
{
cout << e.what() << endl;
}
运行结果如图:
总结: operator new 对比 malloc 使用方式相同, 处理错误的方式不同
new本身就是为了C++面向对象编程而产生的, 因此从malloc->operator new->new是层层递进的
malloc
operator new ==> malloc + 申请空间失败抛异常的实现
new ==> operator new + 自动调用构造函数
对应的择优free, operator delete, delete
而delete 比起 free 不一样的地方就是 delete 自调用析构函数清理资源.
operator delete 与 free没有区别.因为释放空间失败会自动终止程序.
new / delete原理
new原理
- 调用 operator new函数申请空间
- 在申请到的空间上执行自定义类型的构造函数, 完成对象的构造
delete原理
- 在申请到的空间上执行自定义类型的析构函数, 完成资源的清理工作
- 调用operator delete函数释放对象的空间
new T[N]原理
- 实际调用operator new函数申请N个对象的空间
- 在申请到的空间执行N次自定义类型的构造函数
delete[N]原理
- 在申请到的空间上执行N次自定义类型的析构函数, 完成N个对象的资源清理
- 实际调用 operator delete函数释放空间
定位new表达式
定位new表达式就是在已分配的内存空间上调用构造函数初始化一个对象
比如:
class Test
{
public:
Test(int x = 10) :
_a(x)
{
cout << "Test" << endl;
}
int GetVal()
{
return _a;
}
~Test()
{
cout << "~Test()" << endl;
}
private:
int _a;
};
void test3()
{
Test* pT = (Test*)malloc(sizeof(Test));//申请好的内存
new(pT) Test(10);//定位new表达式
cout << pT->GetVal() << endl;
}
总结
malloc / free和new / delete的区别
- malloc / free是函数, new / delete是操作符
- malloc申请空间不会初始化, new可以(自动调用构造函数)
- malloc函数使用时需要手动计算类型大小(sizeof(T)), new不需要, 直接在new后面跟所需空间类型即可
- malloc函数申请空间返回值为void*, 因此使用时要做类型强转, new不需要
- malloc申请失败返回NULL(0), new申请失败抛异常
- 最重要的针对自定义类型, malloc / free只申请和释放空间, new会自动调用构造函数完成对象的初始化, delete会调用自定义类型的析构函数完成资源清理, 再释放空间.