内存管理
电脑上的资源:CPU、内存、磁盘
CPU/GPU
GPU是简单的CPU,做简单运算,数量很多
内存中有许多区域:栈、堆、数据段/静态区、代码段/常量区
栈:局部变量
堆:动态开辟的
静态区:全局变量,静态变量
常量区:常量数据
分区域本质是他们的声明周期不同
C语言内存管理方式
malloc/calloc/realloc/free
void Test ()
{
// 1.malloc/calloc/realloc的区别是什么?
//malloc开空间不初始化
//calloc开空间又初始化
//realloc扩容
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
//不需要,原地扩和异地扩
//原地扩,p2和p3指向同一块空间,在p2的后面开了一块空间,只需要释放p3即可
//异地扩,会把原来p2指向的内容拷贝到p3指向的新空间中,然后会自动free掉p2,所以只需要free p3
free(p3 );
}
C++ new和delete
C++通过new和delete进行操作内存的管理
new和delete都是操作符/关键字
申请和释放单个元素的空间new 和 delete操作符
申请和释放连续的空间,new [] 和 delete[]
new既开空间又调用构造函数(初始化)
delete既释放空间又调用析构函数(销毁)
- new/delete操作内置类型
声明
int* p1 = new int;
//new一个int的对象,声明
int* p2 = new int[10];
//new 10 个int大小的空间
delete p1;
delete[] p2;
申请对象+初始化
int* p3 = new int(0);
//p3指向一个数据为0的空间
int* p4 = new int[10]{0};
//10个数据都初始化为0
int* p5 = new int[10]{1,2,3,4,5};
//前5个初始化,后5个为0
delete p3;
delete[] p4;
delete[] p5;
- new/delete操作自定义类型
C++对于内置类型malloc和free就够用,但是为了自定义类型才使用new/delete,因为他们自动调用构造和析构函数
A* p1 = new A;//默认构造
A* p2 = new A(1);//传参构造
delete p1;
delete p2;
A* p3 = new A(2,2);
//传多个参数构造
动态开辟的类类型的数组
//没有默认构造,显示(给值)构造的三种方式
A* p1 = new A(1);
A* p2 = new A(2,2);
1.有名对象
A aa1(1,1);//调用构造
A aa2(2,2);
A aa3(3,3);
A* p3 = new A[3]{aa1,aa2,aa3};//拷贝构造
2.匿名对象
A* p4 = new A[3]{A(1,1),A(2,2),A(3,3)};
// A aa = A(1,1) 本来是构造+拷贝构造
//被编译器优化为直接构造
3.隐式类型转换
A* p5 = new A[3]{{1,1},{2,2},{3,3}};
//A aa1 = {1,1}; 隐式类型转换为A类型再用临时对象拷贝构造aa1
//被编译器优化为直接构造
operator new和operator delete函数
operator new 和 operator delete 是系统提供的全局函数,new的底层是operator new申请空间,delete的底层是operator delete释放空间
operator new实际上也是通过malloc来申请空间的,申请空间成功就直接返回,失败就抛异常,需要用try,catch捕捉异常,operator delete通过free来释放空间
平时使用一般不会发生抛异常,如果申请大块内存会发生抛异常
1.申请大块空间
try
{
void* p1 = new char[1024*1024*1024];
cout << p1 << endl;
void* p2 = new char[1024*1024*1024];
cout << p2 << endl;
void* p3 = new char[1024*1024*1024];
cout << p3 << endl;
//开三次大块空间,第一次会成功,后两次失败,抛异常
}
catch(const exception& e)
{
//execption 库里面的一个异常的类型
cout << e.what() << endl;
}
//打印
bad allocation
没有你需要的那么大的内存了
2. 调用的函数异常
void func()
{
int n = 1;
while(1)
{
void* p1 = new char(对象类型)[1024*1024](对象大小)
cout << p1 << "->" << n << endl;
//申请的次数太多
}
}
int main()
{
try
{
func();//函数异常
}
catch(const exception& e)
{
cout << e.what() << endl;
}
}
new 调用operator new,operator new 调用构造函数和malloc,delete 调用operator delete, operator delete 调用析构函数和free
new和delete实现的原理
内置类型
new delete申请释放单个元素空间,new[] delete[]申请释放连续空间,new申请失败抛异常,malloc失败会返回NULL
自定义类型
new
1.调用operator new完成空间申请
2.在申请空间的基础上完成对象构造初始化
delete
1.调用析构完成资源的清理工作
2.调用operator delete完成对象空间释放的工作
定位new表达式(placement-new)
定位new
A* p1 = new A(1);
delete p1;
A* p2 = (A*)operator new(sizeof(A));
//开空间
new(p2)A(1);
//构造要定位new 初始化
p2->~A();
//析构可以显示调用
operator delete(p2);
//释放空间
在平时的场景下根本用不到定位new,而在特殊场景内存池时要用到,因为内存池分配出的空间没有初始化,需要我们手动初始化,就要显示调用构造函数
比如某个业务模块需要高频使用,而每次在堆上申请空间需要等待,所以创建一个内存池给你这个业务专门使用,就能提高效率