1.总结并剖析malloc/free和new/delete之间关系和差异
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回对应类型的指针。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
我们先看一看malloc/free和new/delete如何实现对象的动态内存管理,见下面示例。
#include<iostream>
using namespace std;
class AA
{
public:
AA()
{
cout << "构造函数" <<"AA()" << endl;
}
~AA()
{
cout << "析构函数" <<"~AA()" << endl;
}
void Initialize(void)
{
cout << "模拟构造" << endl;
}
void Destroy(void)
{
cout << "模拟析构" << endl;
}
};
void UseMallocFree(void)
{
AA *a = (AA *)malloc(sizeof(AA)); // 申请动态内存
a->Initialize(); // 初始化
//…
a->Destroy(); // 清除工作
free(a); // 释放内存
}
void UseNewDelete(void)
{
AA *a = new AA; // 申请动态内存并且初始化
//…
delete a; // 清除并且释放内存
}
类Obj的函数Initialize模拟了构造函数的功能,函数Destroy模拟了析构函数的功能。函数UseMallocFree中,由于malloc/free不能执行构造函数与析构函数,必须调用成员函数Initialize和Destroy来完成初始化与清除工作。函数UseNewDelete则简单得多。
所以我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。
既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。
2.剖析new/delete、new[ ]/delete[ ]
注意:
C++的其他内存管理接口
void * operator new (size_tsize);
void operator delete (size_tsize);
void * operator new ;
void operator delete[] (size_tsize);
总结:
1. operator new/operator delete
operator new[]/operator delete[] 和malloc/free用法一样。
2. 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。
3. 实际operator new和operator delete只是malloc和free的一层封装,
不是重载 new 和 delete 表达式。
new/delete
new/delete动态管理对象。
new在申请动态内存时会调用operator new分配空间,调用构造函数完成初始化,delete在释放动态内存时会调用析构函数完成清理工作,调用operator delete释放空间,UseNewDelete()测试用例:
int main()
{
UseNewDelete();
system("pause");
}
new[ ]/delete[ ]
new[]/delete[]动态管理对象数组。
new[N]
1. 调用operator new分配空间。
2. 调用N次构造函数分别初始化每个对象。
delete[]
1. 调用N次析构函数清理对象。(思考这里怎么N是怎么来的?)
2. 调用operator delete释放空间。
实现NEW_ ARRAY/DELETE_ARRAY宏,模拟new[]/delete[]申请和释放数组
实现NEW_ ARRAY/DELETE_ARRAY宏,模拟new[]/delete[]申请和释放数组,来看看这两个运算符到底做了些什么事情:
#define NEW_ARRAY(PTR, TYPE, N) /
do{ /
PTR = (TYPE*)operator new(sizeof(TYPE)*N + 4); /
(*(int*)PTR) = N;PTR = (TYPE*)((char*)PTR + 4); /
for (size_t i = 0; i < N; i++) /
new(PTR+i)TYPE; /
} while (false);
#define DELETE_ARRAY(PTR, TYPE) /
do{ /
int N = *((int*)PTR-1); /
for (size_t i = 0; i < N; i++) /
PTR[i].~TYPE(); /
PTR = (TYPE*)((char*)PTR - 4); /
operator delete(PTR); /
} while (false);
补充:
1.new(PTR+i)TYPE;
定位new表达式(replacement版本)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
new (place_address) type
new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表。
2.do{
}while(false) 是为了生成一个局部域,定义和使用临时变量,避免变量重定义。