目录
3.operator new和operator delete
1.C++的内存分布
- 栈区:在执行函数的时候,栈的生长方向是向下的,也就是向着内存地址减小的方向,并且是由编译器自动管理,无需我们手工控制。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
- 堆区:对于堆来讲,它的生长方向是向上的,是向着内存地址增加的方向增长,并且释放工作由程序员控制。若申请了堆上的空间,不释放,很容易造成内存泄漏。
- 常量区:存放常量(程序在运行的期间不能够被改变的量,例如: 10,字符串常量”abcde”, 数组的名字等)
- 静态区:静态变量和全局变量的存储区域是一起的,一旦静态区的内存被分配, 静态区的内存直到程序全部结束之后才会被释放
接下来我们看一道题目
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";
char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof (int)*4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int)*4);
free (ptr1);
free (ptr3);
}
问题及答案如下
1. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?__C__ staticGlobalVar在哪里?__C__
staticVar在哪里?__C__ localVar在哪里?__A__
num1 在哪里?__A__
char2在哪里?__A__ *char2在哪里?__A__
pChar3在哪里?__A__ *pChar3在哪里?__D__
ptr1在哪里?__A__ *ptr1在哪里?__B__
2. 填空题:
sizeof(num1) = __40__;//数组大小,10个整形数据一共40字节
sizeof(char2) = __5__;//包括\0的空间
strlen(char2) = __4__;//不包括\0的长度
sizeof(pChar3) = __4__;//pChar3为指针
strlen(pChar3) = __4__;//字符串“abcd”的长度,不包括\0的长度
sizeof(ptr1) = __4__;//ptr1是指针
globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区
staticVar静态局部变量在静态区 localVar局部变量在栈区
num1局部变量在栈区
char2局部变量在栈区
char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上
pChar3局部变量在栈区 *pChar3得到的是字符串常量字符在代码段
ptr1局部变量在栈区 *ptr1得到的是动态申请空间的数据在堆区
如下给出一幅图
2.C++的动态内存管理
学C语言的时候我们学习了malloc,relloc,calloc,free等函数,C++兼容C语言,也可以在C++中运用,但运用起来非常的麻烦,C++提出了自己的内存管理方式:通过new,delete关键字进行动态内存管理
new和delete操作内置类型
int main()
{
//C语言版
int* p1 = (int*)malloc(sizeof(int));
int* p2 = (int*)malloc(sizeof(int) * 10);
free(p1);
free(p2);
//C++版
int* p3 = new int;
int* p4 = new int[10];
int* p5 = new int(1); //对p5初始化
int* p6 = new int[10] {1, 2, 3, 4};//对p6初始化
delete p3;
delete[]p4;
}
申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[ ]和delete[ ]。
对内置类型,malloc和new其实没什么差别,他们的差别体现在自定义类型。
new和delete操作自定义类型
- malloc函数只能开空间,而new既能开空间,还能调用构造函数
- delete会调用析构函数,free不会
- (如果对构造函数,析构函数不熟悉的可以参考默认构造函数)
malloc和free
malloc只是开辟了空间并没有初始化
我们看看new和delete
new不紧开辟了空间,还调用了构造函数。
delete调用了析构函数
还记得我们当时学习链表的时候,创建一个新的结点非常麻烦,现在学习了new简便多了。
3.operator new和operator delete
operator new是对malloc的封装,operator delete本质是封装了free,底层原理是一样的。
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
注意:operator new和operator delete 不是new和delete的重载,它们是库函数。
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
操作operator new和operator delete
int main() { Stack* ps2 = (Stack*)operator new(sizeof(Stack)); operator delete(ps2); Stack* ps1 = (Stack*)malloc(sizeof(Stack)); assert(ps1); free(ps1); }
operator new和operator delete的功能和malloc、free一样。也不会去调用构造函数和析构函数,不过还是有区别的,1、operator new不需要检查开辟空间的合法性。2、operator new开辟空间失败就抛异常。
new其实就是malloc(opeartor new)和构造函数的结合
调用operator new函数申请空间,在申请空间上执行构造函数,完成对象构造。
我们转为反汇编的时候也能明显的看到用到了operator new。
同理,delete就是 operator delete和析构函数的结合
在空间上执行析构函数,完成对对象中的资源清理,调用operator delete函数释放对象空间。
4.定位new
定位new其实是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化
class Test { public: Test(int date = 2) : _data(date) { cout << "Test():" << this << endl; } ~Test() { cout << "~Test():" << this << endl; } private: int _data; }; int main() { // pt现在指向的只不过是与Test对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行 Test* pt1 = (Test*)malloc(sizeof(Test)); //new (place_address) type new(pt1)Test; // 注意:如果Test类的构造函数有参数时,此处需要传参 //new(place_address) type(initializer - list) Test* pt2 = (Test*)malloc(sizeof(Test)); new(pt2)Test(10); }