赞
踩
C语言和c++都对内存进行了划分,本质上是由操作系统划分的。划分的这部分内存叫做进程虚拟地址空间,本质上是虚拟的地址。还要通过一系列操作和物理地址相关联。
数据段是系统的叫法,语言上的叫法叫静态区。
代码段是系统的叫法,语言上的叫法叫常量区。代码段的代码不是程序员写的代码,而是代码编译好后执行的二进制指令。CPU从这里取指令来执行,有寄存器控制执行的位置,寄存器会记录执行到哪一条指令。作为语言上的常量区,它负责储存一些常量。比如字符串常量。
一个可执行程序(文件,在磁盘中)包含全局数据/静态数据会在静态区开辟好,编译好的指令会被加载到常量区,栈区/堆区的数据是在运行时才开辟。一个程序运行起来就会在内存中开辟一个进程虚拟地址空间。这个空间就是给程序用的。从操作系统角度,每一个程序运行起来都是进程。
C语言是通过malloc,calloc,realloc,free来进行动态内存管理的,c++兼容了C语言的语法,但是c++也有c++的动态内存管理方式:new,delete。(new和delete是操作符)说明c++的方式肯定有存在的必要。new和delete是为了解决C语言动态内存管理中一些不好用的地方。
new的简单用法:
int main()
{
int* p1 = new int;//new1个对象
int* p2 = new int[10];//new10个对象,c++98[]没有初始化方式,c++11才出现初始化方式
int* p3 = new int(10);//new1个对象,并初始化为10
int* p4 = new int[10]{10, 1, 2}
//c++11支持的语法,new10个对象,并将第1,2,3个数初始化为10,1,2。
delete p1;//new出来的要delete
delete[] p2;
//new带[]出来的,delete也要加上[]。要匹配,不匹配可能会报错,也可能崩溃,也可能没有影响。
delete p3;
delete[] p4;
return 0;
}
对内置类型而言,malloc和new除了用法不同,没有什么太大区别。有区别的地方在自定义类型。
对C语言来说,开辟一个自定义空间需要:
struct ListNode
{
int _val;
struct ListNode* _next;
};
struct ListNode* buyListNode (int x)
{
struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
assert(node);
node->_val = x;
node->_next = NULL;
return node;
}
int main()
{
struct ListNode* n1 = buyListNode(1);
return 0;
}
而对c++来说
struct ListNode
{
int _val;
ListNode* _next;
ListNode(int x = 0)
:_val(x)
,_next(nullptr)
{}
};
int main()
{
ListNode* n1 = new ListNode(1);
return 0;
}
对自定义类型来说,C语言malloc开辟空间不初始化,c++new开辟空间并调用构造函数初始化。而且malloc失败后返回0,new失败后抛异常。所以new不需要检查。
struct构造的类和class构造的类相比,更适用于每个节点都可以被访问的情况。比如带头双向循环列表,这个数据结构要求每个节点都能被访问到。
struct ListNode
{
int _val;
ListNode* _next;
ListNode* _prev;
ListNode(int x = 0)
:_val(x)
,_prev(nullptr)
,_next(nullptr)
{}
};
class List
//head节点是链表类的私有,在链表类中可以可以访问,要取出数据,通过成员函数接口实现。
{
public:
List()
{
_head = new ListNode;//new和malloc的最大区别:会调用自定义类型的构造函数进行初始化
_head->next = _head;
_head->prev = _head;
}
private:
ListNode* _head;
};
int main()
{
ListNode* n1 = new List;
delete n1;
//delete和free不同的地方在于会调用类的析构函数,即:如果存在自定义类型的成员变量,除了会销毁n1指向的空间,还会调用自定义类型的析构函数
return 0;
}
new开辟失败会抛异常:
框内部分的意思是:抛异常,没有捕获。
捕获的方式——先了解一下,异常的机制涉及继承和多态
将可能抛异常的地方包含在try中
捕获后运行的结果
operator new和operator delete函数
operator new和operator delete是库函数,但不是函数重载,偏偏有operator,这是c++上一个不合理的点。
框出的部分是抛异常的
这是operator new的源码,由图中可知,operator new实际上是由malloc封装得到的。malloc失败,抛异常。
operator delete的源码。delete是由_free_dbg封装的,_free_dbg就是free函数,free函数在C语言上是一个宏函数。
operator new和malloc在功能上是一样的,operator new返回的是void*,所以也需要强转。
执行上面的代码,发现在operator new中没有调用构造函数。在使用方面,malloc和operator new是一样的。
operator new和malloc的区别在于,operator new失败后会抛异常。
和new相比,operator new没有优势,所以operator new并不是我们使用的,而是在new的底层原理中使用的。
Stack* n1 = new Stack;
这行代码转换成指令:开辟空间,调用构造函数,即call malloc,call Stack构造函数。
malloc失败会返回空,但是不满足c++的需求,返回 错误码(值)的方式不怎么舒服,用抛异常的方式更合适。
(面向对象语言的库中都有一个要求:不再用C语言返回 值的方式来处理)
所以call malloc就变成了call operator new
operator new与operator delete的类专属重载(目前只需要了解)
在某些特定的情况下会用到。比如频繁插入链表节点,new要调用operator new,operator new要调用malloc。频繁的new频繁的调用。解决方法是:不向堆申请空间。
有个东西叫池化技术。就是自己建立一个池,比如今后会学到的内存池,进程池。然后向池申请空间。从池中释放空间。
可以理解为向池申请空间相对于向内存申请空间更快。可以提高效率。
但是c++new对象必然会调用构造函数,也就必然会调用new,new会调用库中的operator new。
c++存在一种机制,叫重载专属operator new。
在成员函数中添加:
构造函数中,new就会调用成员函数中的operator new,而不是库中的operator new。operator new中的内容就是自己决定的了。就可以在里面写内存池的机制。如何建立内存池现在不好讲。
new和delete的实现原理
5.1 内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
5.2 自定义类型
new的原理
1. 调用operator new函数申请空间
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来释放空间
定位new(了解)
定位new表达式(placement-new)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
Stack* obj = (Stack*)operator new(sizeof(Stack));
//obj已经开辟,Stack成员为私有,构造函数不能显式调用。这时使用定位new就可以显式调用构造函数完成初始化
new(obj)Stack;//可以传参,new(obj)Stack(4);
这个写法等价于:Stack* obj = new Stack(4);
malloc/free和new/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在释放空间前会调用析构函数完成空间中资源的清理
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。