1.C++的内存分布
【前情提要】我们在编写C++程序的时候,会构建多个函数,他们都会生成栈帧,创建的变量也需要空间进行存放,那我们如何才能知道,自己所创建的内容到底位于哪里呢?接下来我们来了解下C++程序中的内存划分。
1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
4. 数据段--存储全局数据和静态数据。(静态区)
5. 代码段--可执行的代码/只读常量。(常量区)
int globalVar = 1;//全局变量,静态区
static int staticGlobalVar = 1;//静态变量,静态区
void Test()
{
static int staticVar = 1;//静态变量,静态区
int localVar = 1;//局部变量,栈区
int num1[10] = { 1, 2, 3, 4 };//数组名代表整个数组,在栈区
char char2[] = "abcd";
//char2 和 *char2 两者是不同的,
//char2 是数组名,在栈区
//*char2 是对*char2的解引用,是首元素地址,a是首元素,a在栈上
const char* pChar3 = "abcd";
//pChar3是局部变量,在栈上
//const修饰的是*pChar3,推出*pChar3 是首元素地址,是在常量区
int* ptr1 = (int*)malloc(sizeof(int) * 4);//*ptr1动态开辟在堆区,ptr1是栈区
int* ptr2 = (int*)calloc(4, sizeof(int));//同上
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
2.C语言中的内存管理方式:malloc/realloc/calloc/free
void Test ()
{
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
//并不需要,free(p3)就会把p2也销毁了
free(p3);
}
这四者,大体的区别可以简单归为:直接开,初始开,重新开,倒闭。
3.C++的内存管理
3.1 new/delete操作内置类型
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
还可以多次生成对象,对对象初始化,前几个初始化,其他则为默认值。
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。
3.2 new和delete操作自定义类型
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
还会调用构造函数和析构函数
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
free(p1);
delete p2;
// 内置类型是几乎是一样的
int* p3 = (int*)malloc(sizeof(int)); // C
int* p4 = new int;
free(p3);
delete p4;
A* p5 = (A*)malloc(sizeof(A)*10);
A* p6 = new A[10];
free(p5);
delete[] p6;
return 0;
}
new的对象,会去调用默认构造函数,而delete会去调用析构函数。new多少个对象则去调用多少次默认构造函数,delete[]则是调用多次析构函数。
如果没有默认构造该怎么去new[]呢?
class A
{
public:
A(int a1,int a2 = 1)
{
cout<<"A(int a1,int a2 = 1)"<<endl;
}
~A()
{
cout<<"~A()"<<endl;
}
private:
int _a1;
int _a1;
};
int main()
{
//A* p1 = new A;没有默认构造就不支持这样初始化
A* p2 = new A(2,2);
A* p3 = new A(1);
A* p4 = new A[2]{p2,p3};//严格来说这里就不是调用构造了
//是调用拷贝构造了
A* p5 = new A[2]{A(3,3),A(4,4)};//匿名对象也可以
A* p6 = new A[2]{{5,5},{6,6}};//隐式类型转换可以
return 0;
}
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
4. new和delete的实现原理
4.1 内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
4.2 自定义类型
>new的原理:
1. 调用operator new函数申请空间//底层是malloc,相当于malloc plus
2. 在申请的空间上执行构造函数,完成对象的构造//完成初始化
>delete的原理:
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用operator delete函数释放对象的空间
>new T[N]的原理:
1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
2. 在申请的空间上执行N次构造函数
>delete[]的原理:
1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
class A
{
private:
int _a1;
};
int main()
{
int* p1 = new int(1);
free(p1)//这里会不会产生内存泄漏呢???
//答:并不会,内置类型没有析构函数的概念,调用delete就是
//调用operator delete,就是调用free,但是不推荐!!!
A* p2 = new A;
free(p2);//对于自定义类型呢,还能不能那样了
//答:虽然程序不会崩,但是调用的时候并没有调析构
//产生内存泄露了
int* p3 = new int[10];
delete p3;//这个也不会,new和delete都是malloc和free解决
//没有构造和析构的概念
return 0;
}
有了以上的概念,我们看看一段有趣的代码,并解答下问题。
我们将new A的代码屏蔽,只运行new B的,程序可以正常运行。
但我们如果运行A的话,则程序崩溃了,这是什么原因呢????
转到反汇编,我们可以看到,A的大小为84字节,但我们A类只有8字节,10个为80字节,那多出来的那部分,到底是哪里来的呢?