欢迎各位大佬光临本文章!!!
还请各位大佬提出宝贵的意见,如发现文章错误请联系冰冰,冰冰一定会虚心接受,及时改正。
本系列文章为冰冰学习编程的学习笔记,如果对您也有帮助,还请各位大佬、帅哥、美女点点支持,您的每一分关心都是我坚持的动力。
我的博客地址:bingbing~bang的博客_CSDN博客https://blog.csdn.net/bingbing_bang?type=blog
我的gitee:冰冰棒 (bingbingsupercool) - Gitee.comhttps://gitee.com/bingbingsurercool
系列文章推荐
目录
3.operator new和operator delete
前言
在学习C语言时我们对动态内存申请与释放重点学习了几个函数,malloc、calloc、realloc、free。那么C++种是否含有这种类似的内存管理函数呢?答案是肯定的,只不过C++中兼容C语言的这些函数,并且C++又提出了两个关键字进行管理,new与delete。new关键字用来动态开辟,delete用来释放。
1.C/C++中的内存分布
C/C++中的内存分布图如下图所示:
(1)栈:又叫堆栈,非静态局部变量,函数参数,返回值等等,栈是向下增长的,由高地址向低地址延申。
(2)内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
(3)堆:用于程序运行时动态内存分配,堆是向上增长的,由低地址向高地址延申。
(4)BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
(5)数据段:存储已初始化的全局数据和静态数据,static修饰的变量一般存储在此。
(6)代码段:可执行的代码/只读常量,常量字符串存储位置。
通过下面的例题来分析:
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";
const 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);
}
globalVar,staticglobalVar都是全局变量,并且staticClobalVar被static修饰成为静态全局变量,因此二者都存储于数据段中。staticVar本来是局部变量,但是被static修饰后变为静态局部变量,存储于数据段。localVar,num1,char2,pchar3,ptr1,ptr2,ptr3都是在栈上开辟的局部变量,只是指针指向的内容不同。char2是个数组,数组内容被字符串进行初始化,但是内容还是存在于栈上。pchar3指向一个常量字符串,因此*pchar3指向的内容存储于常量区,也就是代码段,*pchar3同理。*ptr1,*ptr2,*ptr3指向的内容均为在堆上开辟的空间。
2.C++中的内存管理方式
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
2.1内置类型处理
对于内置类型,new和delete与malloc和free并无本质区别,只有使用方法的区别。new的使用方法简单,并且不用计算字节大小,只需后面跟开辟类型即可。delete后只需跟上指针。
int main()
{
int* p1 = new int;//开辟一个int空间
int* p2 = new int(5);//开辟一个int空间并初始化为5
int* p3 = new int[5];//开辟5个int空间
int* p4 = new int[5]{ 1,2,3 };
//开辟5个空间,前三个初始化为1,2,3,剩余的为0;
delete p1;
delete p2;
delete[] p3;//释放多个空间
delete[] p4;
return 0;
}
2.2自定义类型
之所以C++要使用new和delete是在于对自定义类型处理时,new于delete不仅完成了申请和释放的工作,还调用了对象的构造函数和析构函数对其进行了初始化和清理。
并且,new在开辟空间失败时会抛异常而不是像malloc一样返回空指针。
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a=0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A* p3 = new A(3);//开辟一个A类型的空间并调用构造函数初始化为3
A* p4 = new A[10]{ {1},{2},{3},{4} };//开辟多个并构造
A* p5 = new A[5]{ A(10),A(20) };//开辟多个并构造
delete p3;
delete[] p4;
delete[] p5;
return 0;
}
在使用new与delete时,一定要注意类型匹配,new申请的是数组,delete释放时就需要加上 [ ]释放,避免错误。
3.operator new和operator delete
这两个函数是系统提供的全局函数,new与delete在开辟和释放空间的时候会自动调用这两个全局函数。底层逻辑还是使用malloc开辟的空间,还是去调用的free释放。
我们可以自己重载这两个函数用来实现特殊的功能,比如将开辟空间和释放空间的代码文件记录到日志中。
实际上我们可以主动调用这两个函数进行空间开辟,但是没有必要,直接使用new和delete即可。
int* p= (int*)operator new (20);//主动调用
operator delete(p);
4.定位new
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
我们现阶段能够调用构造函数的方法只有创建对象和用new开辟空间时可以调用,当我们使用malloc申请一个自定义类型的空间时,没有办法调用构造函数进行初始化,此时我们就可以使用定位new进行初始化。
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a=0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A* p = (A*)malloc(sizeof(A));
A* s = (A*)malloc(sizeof(A));
new (p)A;
new (s)A(20);//初始化值为20
p->~A();//调用析构清理
free(p);
s->~A();//调用析构清理
free(s);
return 0;
}
5.new和malloc以及delete与free的区别
new的原理:
调用operator new开辟空间,再调用构造函数完成对象构造。
delete的原理:
在空间上先执行析构函数,完成对象的清理工作,然后调用operator delete释放对象空间。
new T [N]的原理:
调用operator new[ ]函数,在operator new[ ]中实际调用operator new函数完成N个对象空间的申请,在申请的空间上执行N次构造。
delete [ ]的原理:
先在空间上执行N次析构函数,完成N个对象中的清理,然后调用operator delete[ ]释放空间,实际在operator delete[]中调用operator delete来释放空间。
区别:
(1)malloc和free是函数,new和delete是操作符
(2)malloc申请的空间不会初始化,new可以初始化
(3)malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[]中指定对象个数即可
(4)malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
(5)malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
(6)申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new 在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理