| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> C++ 堆/栈区别,new的用法,以及与malloc的区别 -> 正文阅读 |
|
[C++知识库]C++ 堆/栈区别,new的用法,以及与malloc的区别 |
1、堆与栈的区别首先从虚拟内存分布上可以看出,栈是由高地址向低地址扩展,堆是由低地址向高地址扩展。 在管理上: 堆内存由程序员手动管理,用new/delete 或者 malloc/free 手动进行分配与回收。如果不进行回收的话,会造成内存泄漏的问题。 栈内存是由编译器管理,不需要程序员管理,会自动分配回收空间,一般保存的是局部变量和函数参数。相比于堆,栈的空间小很多,但空间是连续的,不会有内存碎片的存在。 1、函数调用时,栈帧发生的变化?在其中,有一个问题需要注意,在函数调用的时候,对参数的压栈是从参数列表的右边开始压栈。至于为什么从右边进行压栈,而不是从左边进行,有一个说法是,在C++/C 中,有可变参数的函数,如果是从左向右压栈,第?个参数(即描述可变参数表各变类型的那个参数)将被放在 栈底,由于可变参的函数第?步就需要解析可变参数表的各参数类型,即第?步就需要得到上述参数,因此,将它放在栈底是很不?便的。)在函数调用的过程中,也不会产生内存碎片。 2、操作系统管理申请内存实际上系统中有?个空闲链表,当有程序申请的时候,系统遍历空闲链表找到第?个?于等于申请 ??的空间分配给程序,?般在分配程序的时候,也会空间头部写?内存??,?便 delete 回收空间??。当然 如果有剩余的,也会将剩余的插?到空闲链表中,这也是产?内存碎?的原因。 参考:https://zhuanlan.zhihu.com/p/464435500 对于内存池的应用: 在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。 一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗; 另一方面,频繁的分配和释放小块内存会导致大量的内存碎片的产生,当碎片积累到一定的量之后,将无法分配到连续的内存空间,系统不得不进行碎片整理来满足分配到连续的空间,这样不仅会导致系统性能损耗,而且会导致程序对内存的利用率低下。 当然,如果我们的程序不需要频繁的分配和释放小块内存,那就没有使用内存池的必要,直接使用malloc,free或new,delete函数即可。 2、认识new? ? ? ? 在侯捷老师的课程中,new被分为?new?expression;operator?new 和 placement?new 三种。其中new?expression既要分配内存,还有调用构造函数;operator?new仅分配内存,不调用构造函数;placement?new?不分配内存,仅调用构造函数。 在cpp reference中,operator new有三种重载:下面介绍其中的两个,抛异常的new 与 指定地址的new
1、new 的类型和使用定义Myclass类。
new?expression:new/delete
new和delete会先分配内存,然后调用构造函数。因此输出调用的语句如上。 ::operator new()/::operator delete()
上述代码不会输出任何结果,因为operator?new并不会调用构造函数,上面的代码只是创建了一个MyClass类型的指针,然后就释放了,没有其余操作。 placement?new
2、cpp reference的例子
以上代码是c++ reference摘过来的,原文中使用的是delete p3,此时就会调用p3的析构函数,然而p3并未构造任何对象,引发错误如下,摧毁了一个未知区域未定义的对象。改为::operator delete(p3),应为正确。 3、placement new如何用?? ? ? ? placement new是对operator new的重载,对于效率和容错要求比较高的程序来说placement new是很有用的: 1)placement new能够有效的解决内存碎片的问题; 2)malloc、new等申请内存有失败的可能,对有些程序来说是不允许的,而placement new可以做到; 3)placement new不需要执行申请内存的过程,速度更快;
对于buff这块内存可以反复使用,只要重复2)、3)、4)步骤即可。在C++标准中,对于placement operator new []有如下的说明:placement operator new[] needs implementation-defined amount of additional storage to save a size of array. 所以我们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。 4、为什么没有placement delete因为很简单的道理,先看placement new的作用,定位放置:“new (pointer) ClassName”,旨在使用pointer调用构造函数,并不分配内存;如果有对应的placement delete的话就需要只调用析构函数,而不释放内存,而指针是可以直接显式的调用析构函数的(尽管并不推荐)。此时placement delete的设计就会很多余。 5、总结平时使用:new+delete???????
自己写库:operator new+placement new+析构+operator delete
记得operator new和operator delete要成对出现!!??????? 第三四行可以被替代为:
参考:https://mp.weixin.qq.com/s/M2f2k70mZbkaxcl0ZoawcQ 3、new/delete 与 malloc/free 的区别? ? ? ? 它们都用于申请动态内存和释放内存。malloc/free 是 c/c++ 中的标准库函数,new/delete是c++中的运算符。
执行内容 ? ? ? ? malloc,是按字节来分配内存的(意思是分配的大小,由自己指定) ? ? ? ? new,是先开辟内存(由编译器根据你的对象类型,来决定开辟内存的大小),然后对内存进行初始化(执行构造函数),返回正确指向数据的指针。 返回类型 ? ? ? ? malloc,返回类型是void* ,因此在使用的时候,需要强制转换类型;如果申请内存失败,返回nullptr指针; ? ? ? ? new,返回的是申请对象的指针,如果申请内存失败,抛出 bad_alloc类型的异常;因此两者判断是否开辟成功的所做也不一样。
执行过程 ? ? ? ? delete:先调用析构函数,然后释放内存; free 只释放内存。 ? ? ? ? 根据delete的工作步骤,如果是针对于内置数据类型,例如 int,float等,new并不需要执行构造函数;delete并不需要执行析构函数,因此和malloc,free此时并没有什么区别。 参考: https://blog.csdn.net/JACKSONMHLK/article/details/123298801?spm=1001.2014.3001.5501 4、有了malloc/free 为何还需要new/delete?? ? ? ? 对于非内部数据(与内部数据并列理解,内部数据就是编译器本身就认识,比如int、float、char等,非内部数据比如struct,需要自己定义后,编译器才能认识),malloc/free 无法满足动态创建对象的要求。 ? ? ? ?我们希望在创建对象的时候,不仅开辟了内存,同时也完成了构造函数。对象在消亡时,也要自动执行析构函数。而由于 malloc/free是库函数,不在编译器的控制权限之内,也就不能自动执行构造函数与析构函数。所以,在c++中需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理和释放内存工作的运算符delete。 为什么库函数不在编译器的控制范围内? 库函数是已经编译好的代码,编译器不会再去编译检查,由链接器将库与用户写的代码合成obj文件。而运算符,编译器在编译的时候,可以进行判定与操作。 5、STL中,容器是怎样处理内存的?在看c++ STL的时候发现 很多容器例如vector,map,deque等都有分配器allocator,为什么需要分配器呢?我们下面进行讲解: 自己实现一个模板类 vector,不包括 allocator,
从上面的实现中,我们可以看出,在构造一个无指定大小的vector时候,我们传入数据类型,便会直接在堆中默认初始化指定大小个数据类型,之后我们在push_back 的时候,就会执行的是赋值构造函数,而不是拷贝构造函数。下面我们测试一下,我们只是声明一个vector,就将构造函数执行了十次。
上面未加空间配置器的代码,还存在一个问题,当我们pop_back一个数据的时候,只是将last_指针往前移了一位,如果数据在堆中有内存开辟的话,并没有释放,会造成内存泄漏的问题的存在。 从上面,我们可以知道,需要一个空间配置器,它的作用是将容器的内存开辟与数据类型对象构造?分开,将容器的内存释放与数据类型的对象析构分开。定义一个容器的时候,只开辟内存,等到真正push_back的时候,才构造对象,同理,pop_back的时候,析构对象,容器过了生命周期,在释放内存。
|
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 16:31:37- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |