目录
一、new和delete
1.1 new和delete使用
在c++中有自己的内存管理方式new和delete运算符进行动态内存管理。new相当于c语言的malloc申请空间,delete相当于free释放空间。new和delete都是一个运算符。
#include<iostream>
class A
{
public:
//构造
A(int n = 4)
:_a(new int[n])//可以在初始化使用new
,_b(n)
{
std::cout << "A(int n = 4)" << std::endl;
}
//析构
~A()
{
std::cout << "~A()" << std::endl;
delete[] _a;//这里必须用delete[]否则会导致内存泄漏问题
_a = nullptr;
_b = 0;
}
private:
int* _a;
int _b;
};
int main()
{
int* _arr1 = new int;//开辟一个int的空间
int* _arr2 = new int[4];//开辟4个int的连续数组空间
int* _arr3 = new int[4] {1, 2, 3, 4};//初始化一个数组
delete _arr1;
_arr1 = nullptr;
delete[] _arr2;//内置类型不用[]会出现未定义行为,后面会讲
_arr2 = nullptr;
delete[] _arr3;
_arr3 = nullptr;
A* paa1 = new A;//对于类,new一个对象后会调用对应的构造函数
delete paa1;//调用析构函数
paa1 = nullptr;
A* paa2 = new A[4];//创建4个对象
delete[] paa2;
paa2 = nullptr;
A* paa3 = new A[4]{ 1,2,3,4 };
delete[] paa3;
paa3 = nullptr;
A* paa4 = new A[4]{ {1},{2},{3},{4} };
delete[] paa4;
paa4 = nullptr;
return 0;
}
对于new内置类型开辟数组时多参数初始化可以用{}这个。多于delete进行销毁时如果是数组(开辟一个类型的数组就需要在变量前面加[],如果在类中不使用会导致内存泄露问题,因为delete就是销毁的单个对象而delete[]就是销毁数组对象的)。
1.2 new和delete的原理
new原理就是malloc加了一个调用构造函数(实质就是运算符重载,operator new)。对于new[]开辟一个数组大小空间时,也是调用的operator new[],而如果开辟数组后,他内部开辟空间会大于数组大小(前提就是要有析构函数),会多出来4个字节用于储存数组有多少元素(这4个字节存储的是元素个数,位置是该开辟空间的的首地址,这也是为什么必须使用delete[]销毁的原因了,delete对应的是单对象,不会将第一个首元素这个计算数组大小的地址用来销毁,而是跳过他,而如果不从首元素地址销毁会报错),从而为后续delete[](原理operator delete[])进行一个一个销毁。
而对于delete原理就是free加一个析构函数(也是运算符重载operator delete)。delete会调用对应析构函数,但是没有析构函数,它什么都不会进行,当它遇到new[](不会自动计算数组元素个数)时,delete不会立即导致问题,但会显示未定义行为(指的是c加加标准没有明确定义程序该如何执行的情况,就和把披萨放到烤箱里面,而你没设定温度和时间这个未定义,那就是看运气了,如果恰好等你去的时候,打开它那就是完美披萨,相对应的,如果你没有或者超过了那段时间去取那个披萨,此时的披萨就不能吃了)。但是如果有析构函数,它会调用析构函数,对于new[](会自动计算数组元素个数)时,delete会导致内存泄露。
#include<iostream>
class A
{
public:
//构造
A(int n = 4)
:_a(new int[n])//可以在初始化使用new
, _b(n)
{
std::cout << "A(int n = 4)" << std::endl;
}
//析构
~A()
{
std::cout << "~A()" << std::endl;
delete[] _a;//这里必须用delete[]否则会导致内存泄漏问题
_a = nullptr;
_b = 0;
}
private:
int* _a;
int _b;
};
int main()
{
A* paa4 = new A[4]{ {1},{2},{3},{4} };
//std::cout << sizeof(paa4) << std::endl;
delete[] paa4;
paa4 = nullptr;
return 0;
}
二、new、delete和malloc、free的区别
内置类型中动态开辟空间使用new和malloc这些是几乎差不多的(new可以在开辟时初始化,而malloc需要强制类型转换),但是在类置类型中就不一样了(new一次性可以开辟多个对象),new开辟的同时可以调用构造函数,delete会调用析构函数,这些是malloc和free没有的(类置类型数组需要自己初始化时需要用{},不能用()这个避免逗号表达式)。
三、内存分布
解读:
栈:用于存储临时数据、函数调用信息和局部变量
堆:是一种动态分配的内存区域,用于在程序运行时按需分配和释放内存块
数据段:用于存储全局变量和静态数组
代码段:存储的是常量变量。
在这里pChar3这是很典型的常量字符串(不能修改值),它对应的pChar3在栈区域,而它解引用后就是指向的代码段中的常量字符串abcd。而对于char2可以直接拷贝字符串从代码段到栈里面。到这一步了,肯定就会有人问,那为什么int这数组数据不存储在代码段呢,对于int这个数组数据是直接通过我们写的代码指令通过压栈到到数组里面的,所以这也是栈的一部分。
四、栈与堆的区别
栈的静态分配指的是每个函数的栈帧大小在编译时确认。而它的动态分配指的是栈帧的创建和销毁在运行时动态进行,取决于函数调用深度和顺序(递归就是一个例子,包括临时对象也是,在函数内使用临时对象只是针对当前一行代码,使用完就会消失,栈空间会还给操作系统,是的函数栈变小,这也是一个动态过程)。