在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。通常情况不建议使用,但在某些对时间要求非常高的应用中可以考虑,因为选择合适的构造函数完成对象初始化是一个时间相对较长的过程。

点击关注,第一时间了解华为云新鲜技术~


开发者之家
华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

生于云,长于云,让开发者成为决定性力量

1.3k 声望
1.7k 粉丝
0 条评论
推荐阅读
【贺】来自开发者的点赞,华为云开发者联盟入选 2022 中国技术品牌影响力企业榜
2023 年 1 月 4 日,中国技术先锋年度评选 | 2022 中国技术品牌影响力企业榜单正式发布。作为中国领先的新一代开发者社区,SegmentFault 思否依托数百万开发者用户数据分析,各科技企业在国内技术领域的行为及影...

华为云开发者联盟阅读 369

万字避坑指南!C++的缺陷与思考(下)
导读 | 在万字避坑指南!C++的缺陷与思考(上)一文中,微信后台开发工程师胡博豪,分享了C++的发展历史、右值引用与移动语义、类型说明符等内容,深受广大开发者喜爱!此篇,我们邀请作者继续总结其在C++开发过...

腾讯云开发者5阅读 510评论 1

学习下Redis内存模型
redis,对于一个java开发工程师来讲,其实算不得什么复杂新奇的技术,但可能也很少人去深入了解学习它的底层的一些东西。下面将通过对内存统计、内存划分、存储细节、对象类型&内部编码这四个模块来学习学习redis...

京东云开发者1阅读 1.2k

封面图
DBoS 系统说明
程序员TianSong以单片机开发入门,后续又做了 Qt 相关工作,有时间后开始进行 linux 相关的学习,恰巧在二一年十一月份,百问网的韦东山老师进行了三个月的 linux 驱动直播,于是有了开发 DBoS 的念头。

TianSong1阅读 1.1k

【Qt】简单桌面
[链接]简介简单桌面是一款小巧便捷的桌面背景管理软件。由编程爱好者个人开发,不收集使用者个人信息、不连接网络、不弹窗。下载功能支持单静态图片及多静态图片轮播(轮播时间可设置)支持GIF动画背景支持视频背...

TianSong3阅读 2.1k

深入理解 Python 的对象拷贝和内存布局
深入理解 Python 的对象拷贝和内存布局前言在本篇文章当中主要给大家介绍 python 当中的拷贝问题,话不多说我们直接看代码,你知道下面一些程序片段的输出结果吗? {代码...} {代码...} {代码...} {代码...} {代...

一无是处的研究僧阅读 1.1k

封面图
一种将函数模板定义和声明分开的方法
&emsp;&emsp;在 C++ 中为了操作简洁引入了函数模板。所谓的函数模板实际上是建立一个通用函数,其函数类型或形参类型不具体指定,用一个虚拟的类型来表达,这个通用函数就称为函数模板。

Sharemaker阅读 880

封面图

生于云,长于云,让开发者成为决定性力量

1.3k 声望
1.7k 粉丝
宣传栏