首先先看下图了解程序是怎样在内存中管理的:
1.栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2.内存映射段是高效的1O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
3.堆用于程序运行时动态内存分配,堆是可以上增长的
4.数据段一存储全局数据和静态数据。
5.代码段可执行的代码只读常量。
在C语言中我们动态分配内存使用malloc/free;在C++中没有直接使用这种函数,而是使用new / delete 、new[] / delete[]
它们都是动态管理内存的入口,malloc / free是CC++标准库的函数,new/ delete是C++操作符。
为什么C++要使用new / delete 而不使用malloc/free?
因为 malloc / free只是动态分配内存空间释放空间。而new/ delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员),而且malloc/frec需要手动计算类型大小且返回值会void", new / delete可自己计算类型的大小,返回对应类型的指针。
下面剖析new / delete 、new[] / delete[]是如何动态分配内存的:
●new做了两件事1.调用 operator new分配空间。2.调用构造函数初始化对象。
● delete也做了两件事调用析构函数清理对象2.调用 operator delete释放空
●new[N]1.调用 operator new分配空间2.调用N次构造函数分别初始化每个对象。
●
delete[N]1.调用N次析构函数清理对象。(思考这里怎么N是怎么来的2.调用 operator delete释放空间。
new[]在给内置类型开辟空间时,开辟的空间不会多4个字节,而new[]在给自定义类型开辟空间时,而且还看是否有自定义析构函数,如果定义了析构函数才会多开辟四个字节,用来判断这里要调用多少次析构函数;如果没有自定义析构函数,就不会多开辟四个字节了
下面是模拟new / delete 、new[] / delete[]是如何动态分配内存
#include<iostream>
using namespace std;
class A
{
public:
A(int a = 10)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
/*
//模拟new
A* pa = (A*)operator new(sizeof(A)); //开辟空间
new(pa)A(10); //定位new表达式是在已经分配好的原始空间里调用一个构造函数来初始化一个对象
//模拟 delete
pa->~A(); //主动调用析构函数
operator delete(pa); //释放空间
*/
//模拟new[]
/*
int sum = 10;
A* pb = (A*)operator new(sizeof(A)* sum+4); //开辟sum个类成员+4个字节大小的空间
*(int*)pb = sum; //多出来的4个字节,在最前面用来存放sum
//*(int*)pb = sum; 的作用:通过前四个字节里面存的数字判断在后面需要调用多少次构造和析构函数
pb = (A*)((int *)pb + 1);
int i = 0;
for (i = 0; i < *((int*)pb - 1); i++) //显式调用构造函数,通过*((int*)pb-1)可以判断调用多少次构造函数
{
new(pb+i)A(5);
}
// deletep[]
int j = 0;
for (j = 0; j < *((int*)pb-1); j++) //显式调用析构函数,通过*((int*)pb-1)可以判断调用多少次析构函数
{
pb[j].~A();
}
operator delete((int*)pb-1); //释放所以开辟的空间
*/
A* p4 = new A[3];
delete[] p4;
return 0;
}