在C++中,你真的会用new吗?
摘要:“new”是C++的一个关键字,同时也是操作符。关于new的话题非常多,因为它确实比较复杂,也非常神秘。
本文分享自华为云社区《如何编写高效、优雅、可信代码系列(2)——你真的会用new吗》,原文作者:我是一颗大西瓜 。
C++内存管理
1. C++内存分配
C++中的程序加载到内存后按照代码区、数据区、堆区、栈区进行布局,其中数据区又可以分为自由存储区、全局/静态存储区和常量存储区,各区所长如下:
- 栈区
函数执行的时候,局部变量的存储单元都在栈上创建,函数执行结束后存储单元会自动释放。栈内存分配运算内置于处理器指令集中,效率高,但分配内存容量有限。 - 堆区
堆就是new出来的内存块,编译器不管释放,由应用程序控制,new对应delete。如果没释放掉,程序结束后,操作系统会自动回收。 - 自由存储区
C中malloc分配的内存块。用free结束生命周期。 - 全局/静态存储区
全局变量和静态变量被分配到同一块内存中,定义的时候就会初始化。 - 常量存储区
比较特殊的存储区,存放常量,不允许修改。
堆和栈的区别
- 管理方式
栈由编译器自动管理,堆由程序员控制 - 空间大小
32位系统下,堆内存可以达到4GB,栈有一定的空间大小 - 碎片管理
对于堆,频繁的new/delete肯定造成内存空间的不连续,产生大量内存碎片降低程序效率;栈由于遵循先进后出的规则,不会产生空隙 - 生长方向
堆是向上生长的,即向着内存地址增加的方向增长;而栈是向着内存地址减小的方向增长的 - 分配方式
堆是动态分配的,栈有动态分配和静态分配之分:静态分配由编译器完成,动态分配由alloca函数完成,即使是动态分配,依然是编译器自动释放 - 分配效率
计算机底层提供了栈的支持,分配了专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这决定了栈的效率会比较高。堆则是由C/C++函数库提供的,机制比较复杂,比如为了分配某个大小的内存需要在堆内存中搜索可用足够大小的空间,效率比栈要低的多
2. new/delete和new []/delete []
- 回收new分配的单个对象内存空间时用delete,回收用new[]分配的一组对象时用delete[]
- 对于内置类型(int/double/float/char/…),由于new[]申请内存时,编译器还会悄悄在内存中保存整数,表示指针数组的个数,所以delete/delete[]都可以正确释放所申请的内存空间
- 建议在调用new时使用的[],那么调用delete也使用[]
3. new的三种形态
- new operator 常用的new,语言函数内建,不能重载。调用过程中实际完成的有三件事:
- 为类型对象分配内存;
- 调用构造函数初始化内存对象;
- 返回对象指针
如果是在堆上建立对象,直接使用new operator。
- operator new 普通操作符,可以重载。如果仅仅是分配内存,那么应该调用operator new,但不负责初始化。系统默认提供的分配器在时间和空间两方面都存在一些问题:分配器速度较慢,分配小型对象时空间浪费严重,重载new/delete有三方面好处:
- 改善效率
- 检测代码中的内存错误
- 获得内存使用的统计数据
- C++标准规定,重载的operator new必须是类成员函数或全局函数,全局的operator new重载不应该改变原有签名,而是直接无缝替换原有版本。全局重载很有侵略性,别人使用你的库无法使用默认的new,而具体类的重载只会影响本class和其派生类,但是类的operator new函数重载必须声明为static,因为operator new是在类的具体对象被构建出来之前调用的。
- 为了获得2和3的优势,重载的operator new需要如下函数声明void* operator new(size_t, const char* file, int line);
- placement new 定义在库<<new>>中。如果想在一块已经获得内存里建立对象,那么应该调用placement new。通常情况不建议使用,但在某些对时间要求非常高的应用中可以考虑,因为选择合适的构造函数完成对象初始化是一个时间相对较长的过程。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
PostgreSQL11在Windows平台上开启large pages(即:HugePage)
从pg11开始在windows平台上支持large pages(即:HugePage) 测试环境: Windows 10 1909 18363.1198 PostgreSQL 11.10,从edb公司官网下载的,介质名称为“postgresql-11.10-1-windows-x64.exe” 虚拟机环境,分配给虚拟机8G物理内存 开启large pages的步骤 0.安装PostgreSQL 11.10 1.关闭windows uac 控制面板--系统和安全--更改用户账户控制设置--改为'从不通知',点击确定。 2.在windows组策略编辑器,赋予运行PostgreSQL的操作系统用户'锁定内存页'的权力(Lock Pages in Memory) 计算机配置\Windows 设置\安全设置\本地策略\用户权利分配\下的“锁定内存页”这个策略。 3.将windows服务中的postgresql-x64-11这个服务改为手动启动。 4.重新启动OS 5.设置postgresql.conf的配置参数huge_pages=on 6.设置postgresql.conf的配置参数share...
- 下一篇
Java源码分析专题系列之【ConcurrentHashMap】全方位渗透的源码分析(JDK1.8版本)
承接上文 在上一篇文章的时候,已经基本解释了JDK1.7版本的ConcurrentHashMap的核心代码,可见:Java源码分析专题系列之【ConcurrentHashMap】深入浅出的源码分析(JDK1.7版本),接下来我们要研究一下目前非常重要的JDK1.8版本的ConcurrentHashMap,这是目前我们最应该学习的技术源码之一。 前提概要 ConcurrentHashMap是concurrent家族中的一个类,由于它可以高效地支持并发操作,以及被广泛使用,经典的开源框架Spring的底层数据结构就是使用ConcurrentHashMap实现的。 与同是线程安全的老大哥HashTable相比,它已经更胜一筹,因此它的锁更加细化,而不是像HashTable一样为几乎每个方法都添加了synchronized锁,这样的锁无疑会影响到性能。 原理简介 本文的分析的源码是JDK8的版本,与JDK7的版本有很大的差异。实现线程安全的思想也已经完全变了,它摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。 它沿用了与它同时期(JDK1.8)的HashMap...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境