堆栈更喜欢堆?

时间:2015-02-07 17:39:29

标签: c++ class object

我最近参与了图形编程,我注意到许多图形引擎(即Ogre)以及许多整体编码器都喜欢动态地初始化类实例。以下是Ogre Basic Tutorial 1

的示例
//...

Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");

//...
然后将

ogreHeadheadNode数据成员和方法称为ogreHead->blabla

为什么要乱用对象指针而不是普通对象?

顺便说一下,我还读到了堆内存分配比堆栈内存分配慢得多的地方。

3 个答案:

答案 0 :(得分:5)

堆分配不可避免地比堆栈分配慢得多。更多关于“慢多少?”后来。但是,在许多情况下,选择是“为你做的”,原因如下:

  1. 堆栈有限。如果你用完了,应用程序几乎总是被终止 - 没有真正好的恢复,甚至打印错误信息说“我跑出堆栈”可能很难......
  2. 当您离开分配函数时,堆栈分配“消失”。
  3. 可变性更明确,易于处理。 C ++不能很好地处理“可变长度数组”,并且肯定不能保证在所有编译器中都能工作。
  4. 堆栈堆栈的速度有多慢?

    我们会稍微谈谈“并且重要”。

    对于给定的分配,堆栈分配只是一个减法运算[1],其中最小newmalloc将是一个函数调用,甚至可能是最简单的分配器几十个指令,在复杂的情况下成千上万[因为内存必须从操作系统中获取,并清除它以前的内容]。所以从10倍到“无限”慢的任何东西,给予或接受。确切的数字将取决于运行代码的确切系统,分配的大小,以及通常“以前对分配器的调用”(例如,“释放”分配的长列表可能会使分配新对象的速度变慢,因为它非常适合必须搜索)。当然,除非你使用堆管理的“鸵鸟”方法,否则你还需要释放对象并应对“内存不足”,这会增加执行代码/时间和代码的复杂性。

    然而,通过一些相当聪明的编程,这可能大部分都是隐藏的 - 例如,在对象的生命周期内分配长时间分配的东西,将“无需担心”。对于3D游戏中的每个像素或每个三角形,从堆中分配对象将是一个坏主意。但是如果对象的生命周期是很多帧甚至整个游戏,那么分配和释放它的时间几乎为零。

    类似地,不是进行10000个单独的对象分配,而是为10000个对象创建一个。对象池就是这样一个概念。

    此外,通常分配时间不是花费时间的地方。例如,从磁盘中读取文件中的三角形列表所花费的时间比为同一个三角形列表分配空间要长得多 - 即使您分配了每个单独的三角形列表!

    对我来说,规则是:

    1. 它是否适合堆叠?通常几千字节很好,很多千字节不太好,兆字节绝对不行。
    2. 是否已知数字(例如对象数组),以及您可以将其放入堆栈的最大值?
    3. 你知道对象是什么吗?换句话说,可能需要在堆上分配抽象/多态类。
    4. 它的寿命是否与它的范围相同?如果没有,请使用堆(或进一步向下堆栈,并将其传递给堆栈)。
    5. [1]或者添加堆栈是否“向高地址增长” - 我不知道有这样一种架构的机器,但可以想象,我认为已经有了一些。对于堆栈增长的方式,或者关于运行时堆栈如何工作的其他任何方式,C当然不做任何承诺。

答案 1 :(得分:4)

堆栈的范围是有限的:它只存在于一个函数中。现在,现代用户界面程序通常是事件驱动的,这意味着调用您的函数来处理事件,然后该函数必须返回以便程序继续运行。因此,如果您的事件处理函数希望创建一个在函数返回后仍然存在的对象,显然,该对象不能在该函数的堆栈上分配,因为它会在函数返回后立即停止存在。这是我们在堆上分配东西的主要原因。

还有其他原因。

有时,编译时不知道类的确切大小。如果类的确切大小未知,则无法在堆栈上创建,因为编译器需要准确了解需要为堆栈中的每个项目分配多少空间。

此外,经常使用像whatever::createEntity()这样的工厂方法。如果必须调用单独的方法为您创建对象,则无法在堆栈上创建该对象,原因在本答案的第一段中有所解释。

答案 2 :(得分:1)

为什么指针代替对象?

因为指针有助于加快速度。如果按值传递对象,则传递给另一个函数,例如

shoot(Orge::Entity ogre)

而不是

shoot(Orge::Entity* ogrePtr)

如果ogre不是指针,那么您将整个对象传递给函数而不是引用。如果编译器没有进行优化,那么您将得到一个效率低下的程序。还有其他原因,使用指针,您可以修改传入的对象(一些争论引用更好,但这是一个不同的讨论)。否则你会花太多时间来回复制修改过的对象。

为什么要堆?

  1. 在某种意义上,堆是一种更安全的内存类型,可以让您安全地重置/恢复。如果您拨打新电话并且没有内存,则可以将其标记为错误。如果你正在使用堆栈,实际上没有什么好方法可以知道你造成了stackoverflow,没有其他一些监督程序,此时你已经处于危险区域。
  2. 取决于您的申请。堆栈具有本地范围,因此如果对象超出范围,它将释放对象的内存。如果你需要其他功能中的对象,那么没有真正的方法可以做到这一点。
  3. 对OS应用更多,堆比堆栈大得多,特别是在多线程应用程序中,每个线程的堆栈大小都有限。