C++ 自由存储区是否等价于堆?

“free store” VS “heap”

当我问你C++的内存布局时,你大概会回答:

“在C++中,内存区分为5个区,分别是堆、栈、自由存储区、全局/静态存储区、常量存储区”。

如果我接着问你自由存储区与堆有什么区别,你或许这样回答:

“malloc在堆上分配的内存块,使用free释放内存,而new所申请的内存则是在自由存储区上,使用delete来释放。”

这样听起来似乎也没错,但如果我接着问:

自由存储区与堆是两块不同的内存区域吗?它们有可能相同吗?

你可能就懵了。

事实上,我在网上看的很多博客,划分自由存储区与堆的分界线就是new/delete与malloc/free。然而,尽管C++标准没有要求,但很多编译器的new/delete都是以malloc/free为基础来实现的。那么请问:借以malloc实现的new,所申请的内存是在堆上还是在自由存储区上?

从技术上来说,堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。我们所需要记住的就是:

堆是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。

问题的来源

再回过头来来看看这个问题的起源在哪里。最先我们使用C语言的时候,并没有这样的争议,很明确地知道malloc/free是在堆上进行内存操作。直到我们在Bjarne Stroustrup的书籍中数次看到free store (自由存储区),说实话,我一直把自由存储区等价于堆。而在Herb Sutter的《exceptional C++》中,明确指出了free store(自由存储区) 与 heap(堆) 是有区别的。关于自由存储区与堆是否等价的问题讨论,大概就是从这里开始的:

Free Store The free store is one of the two dynamic memory areas, allocated/freed by new/delete. Object lifetime can be less than the time the storage is allocated; that is, free store objects can have memory allocated without being immediately initialized, and can be destroyed without the memory being immediately deallocated. During the period when the storage is allocated but outside the object's lifetime, the storage may be accessed and manipulated through a void* but none of the proto-object's nonstatic members or member functions may be accessed, have their addresses taken, or be otherwise manipulated.

Heap The heap is the other dynamic memory area, allocated/freed by malloc/free and their variants. Note that while the default global new and delete might be implemented in terms of malloc and free by a particular compiler, the heap is not the same as free store and memory allocated in one area cannot be safely deallocated in the other. Memory allocated from the heap can be used for objects of class type by placement-new construction and explicit destruction. If so used, the notes about free store object lifetime apply similarly here.

来源:http://www.gotw.ca/gotw/009.htm

作者也指出,之所以把堆与自由存储区要分开来,是因为在C++标准草案中关于这两种区域是否有联系的问题一直很谨慎地没有给予详细说明,而且特定情况下new和delete是按照malloc和free来实现,或者说是放过来malloc和free是按照new和delete来实现的也没有定论。这两种内存区域的运作方式不同、访问方式不同,所以应该被当成不一样的东西来使用。

结论

  • 自由存储是C++中通过new与delete动态分配和释放对象的抽象概念,而堆(heap)是C语言和操作系统的术语,是操作系统维护的一块动态分配内存。
  • new所申请的内存区域在C++中称为自由存储区。藉由堆实现的自由存储,可以说new所申请的内存区域在堆上。
  • 堆与自由存储区还是有区别的,它们并非等价。

假如你来自C语言,从没接触过C++;或者说你一开始就熟悉C++的自由储存概念,而从没听说过C语言的malloc,可能你就不会陷入“自由存储区与堆好像一样,好像又不同”这样的迷惑之中。这就像Bjarne Stroustrup所说的:

usually because they come from a different language background.

大概只是语言背景不同罢了。

参考资料

游戏引擎架构 [Jason Gregory] 电子工业出版社

exceptional C++ [Herb Sutter]

相关链接: http://www.gotw.ca/gotw/009.htm http://zamanbakshifirst.blogspot.tw/2007/02/c-free-store-versus-heap.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏瓜大三哥

UVM模型(四)

UVM模型(四) ? 1.常用到的uvm_component uvm_driver:所有的driver都要派生自uvm_driver。driver的功能...

2489
来自专栏Java架构沉思录

Kafka中的时间轮算法

简单说说时间轮吧,它是一个高效的延时队列,或者说定时器。实际上现在网上对于时间轮算法的解释很多,定义也很全,这里引用一下朱小厮博客里出现的定义:

1012
来自专栏更流畅、简洁的软件开发方式

【自然框架】注册会员活动——第一份代码的修改建议(第一版)

  感谢“好坏”提供代码,这是我看过的比较不错的三层结构的代码了,业务层并不是直接调用DAL,而是有其自身的逻辑判断,并不是传声筒,很赞。 我对这份代码,按照自...

2216
来自专栏wOw的Android小站

[设计模式]之五:职责链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

1112
来自专栏GreenLeaves

Oracle pl/sql编程值控制结构

以下测试案例均来自于scott方案,运行前请确保该方案解锁。 1、条件分支状语 pl/sql中提供了三种条件分支状语,分别是: if   then if   t...

1949
来自专栏蓝天

高质量C++编程补充条款

介绍高质量C++编程的书籍很多,而且都非常好,这里主要针对已有书籍较少涉及到的代码格式条款进行补充。代码是程序员脸面,清清爽爽和干干净净的代码是程序员高职业素质...

1032
来自专栏C语言及其他语言

【编程经验】C语言中EOF是什么意思

C语言中EOF的意思 今天跟大家说道说道这个C语言中EOF是什么意思。 相信很多朋友在学习C语言过程中,都看到过EOF的字样,但翻过整本C语...

3747
来自专栏编程思想之异常处理

C++异常处理

0.如果使用普通的处理方式:ASSERT,return等已经    足够简洁明了,请不要使用异常处理机制.

941
来自专栏Java开发者杂谈

分布式改造剧集1

背景介绍 ​ 我所在的项目组,使用的技术一直是接近原始社会的:jdk1.6 + SpringMVC + hessian + Mybatis,当前最火的中间件技术...

2924
来自专栏Spark生态圈

[spark] spark推测式执行

推测任务是指对于一个Stage里面拖后腿的Task,会在其他节点的Executor上再次启动这个task,如果其中一个Task实例运行成功则将这个最先完成的Ta...

1682

扫码关注云+社区