动态内存分配
-
new分配动态内存
:使用自由空间分配得内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向一个该对象指针. -
delete释放动态内存
:堆区分配内存,必须手动释放,避免内存泄漏:原型
void *operator new(size_t); //allocate an object
void *operator delete(void *); //free an object
void *operator new[](size_t); //allocate an array
void *operator delete[](void *); //free an array
(new和delete 为C++ 定义的关键字,通过特定的语法可以组成表达式)
底层解析
:举例
class A
{
public:
A(int v) : var(v)
{
fopen_s(&file, "test", "r");
}
~A()
{
fclose(file);
}
private:
int var;
FILE *file;
};
,类 A 中有两个私有成员,有一个构造函数和一个析构函数,构造函数中初始化私有变量 var 以及打开一个文件,析构函数关闭打开的文件。
new
class A *pA = new A(10);
创建一个类的对象,返回其指针 pA。如下图所示 new 背后完成的工作:
new底层可解析为三步
(1)首先需要调用上面提到的 operator new 标准库函数,传入的参数为 class A 的大小,这里为 8 个字节,至于为什么是 8 个字节,你可以看看《深入 C++ 对象模型》一书,这里不做多解释。这样函数返回的是分配内存的起始地址,这里假设是 0x007da290。
(2)上面分配的内存是未初始化的,也是未类型化的,第二步就在这一块原始的内存上对类对象进行初始化,调用的是相应的构造函数,这里是调用 A:A(10); 这个函数,从图中也可以看到对这块申请的内存进行了初始化,var=10, file 指向打开的文件。
(3)最后一步就是返回新分配并构造好的对象的指针,这里 pA 就指向 0x007da290 这块内存,pA 的类型为类 A 对象的指针。
delete
delete pA;
delete 底层分解两步:
(1)调用 pA 指向对象的析构函数,对打开的文件进行关闭
(2)通过上面提到的标准库函数 operator delete 来释放该对象的内存,传入函数的参数为 pA 的值,也就是 0x007d290
那么 delete pA; 做了两件事:
调用一次 pA指向的对象的析构函数;
调用 operator delete(pA); 释放内存
申请和释放数组
string *psa = new string[10]; //array of 10 empty strings
int *pia = new int[10]; //array of 10 uninitialized ints
delete [] psa;
delete [] pia;
注意:(new)上面在申请一个数组时都用到了 new [] 这个表达式来完成,按照我们上面讲到的 new 和 delete 知识,
第一个数组是 string 类型,分配了保存对象的内存空间之后,将调用 string 类型的默认构造函数依次初始化数组中每个元素;
第二个是申请具有内置类型的数组,分配了存储 10 个 int 对象的内存空间,但并没有初始化。
(delete)都用到 delete [] 表达式,注意这地方的 [] 一般情况下不能漏掉!
我们也可以想象这两个语句分别干了什么:第一个对 10 个 string 对象分别调用析构函数,
然后再释放掉为对象分配的所有内存空间;第二个因为是内置类型不存在析构函数,直接释放为 10 个 int 型分配的所有内存空间。
new[ ]
class A *pAa = new A[3];
delete[ ]
delete []pAa;
注意两点是:
(1)调用析构函数的次数是从数组对象指针前面的 4 个字节中取出;
(2)传入 operator delete[] 函数的参数不是数组对象的指针 pAa,而是 pAa 的值减 4。