我最近参与了图形编程,我注意到许多图形引擎(即Ogre)以及许多整体编码器都喜欢动态地初始化类实例。以下是Ogre Basic Tutorial 1
的示例//...
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
//...
然后将 ogreHead
和headNode
数据成员和方法称为ogreHead->blabla
。
为什么要乱用对象指针而不是普通对象?
顺便说一下,我还读到了堆内存分配比堆栈内存分配慢得多的地方。
答案 0 :(得分:5)
堆分配不可避免地比堆栈分配慢得多。更多关于“慢多少?”后来。但是,在许多情况下,选择是“为你做的”,原因如下:
堆栈堆栈的速度有多慢?
我们会稍微谈谈“并且重要”。
对于给定的分配,堆栈分配只是一个减法运算[1],其中最小new
或malloc
将是一个函数调用,甚至可能是最简单的分配器几十个指令,在复杂的情况下成千上万[因为内存必须从操作系统中获取,并清除它以前的内容]。所以从10倍到“无限”慢的任何东西,给予或接受。确切的数字将取决于运行代码的确切系统,分配的大小,以及通常“以前对分配器的调用”(例如,“释放”分配的长列表可能会使分配新对象的速度变慢,因为它非常适合必须搜索)。当然,除非你使用堆管理的“鸵鸟”方法,否则你还需要释放对象并应对“内存不足”,这会增加执行代码/时间和代码的复杂性。
然而,通过一些相当聪明的编程,这可能大部分都是隐藏的 - 例如,在对象的生命周期内分配长时间分配的东西,将“无需担心”。对于3D游戏中的每个像素或每个三角形,从堆中分配对象将是一个坏主意。但是如果对象的生命周期是很多帧甚至整个游戏,那么分配和释放它的时间几乎为零。
类似地,不是进行10000个单独的对象分配,而是为10000个对象创建一个。对象池就是这样一个概念。
此外,通常分配时间不是花费时间的地方。例如,从磁盘中读取文件中的三角形列表所花费的时间比为同一个三角形列表分配空间要长得多 - 即使您分配了每个单独的三角形列表!
对我来说,规则是:
[1]或者添加堆栈是否“向高地址增长” - 我不知道有这样一种架构的机器,但可以想象,我认为已经有了一些。对于堆栈增长的方式,或者关于运行时堆栈如何工作的其他任何方式,C当然不做任何承诺。
答案 1 :(得分:4)
堆栈的范围是有限的:它只存在于一个函数中。现在,现代用户界面程序通常是事件驱动的,这意味着调用您的函数来处理事件,然后该函数必须返回以便程序继续运行。因此,如果您的事件处理函数希望创建一个在函数返回后仍然存在的对象,显然,该对象不能在该函数的堆栈上分配,因为它会在函数返回后立即停止存在。这是我们在堆上分配东西的主要原因。
还有其他原因。
有时,编译时不知道类的确切大小。如果类的确切大小未知,则无法在堆栈上创建,因为编译器需要准确了解需要为堆栈中的每个项目分配多少空间。
此外,经常使用像whatever::createEntity()
这样的工厂方法。如果必须调用单独的方法为您创建对象,则无法在堆栈上创建该对象,原因在本答案的第一段中有所解释。
答案 2 :(得分:1)
为什么指针代替对象?
因为指针有助于加快速度。如果按值传递对象,则传递给另一个函数,例如
shoot(Orge::Entity ogre)
而不是
shoot(Orge::Entity* ogrePtr)
如果ogre不是指针,那么您将整个对象传递给函数而不是引用。如果编译器没有进行优化,那么您将得到一个效率低下的程序。还有其他原因,使用指针,您可以修改传入的对象(一些争论引用更好,但这是一个不同的讨论)。否则你会花太多时间来回复制修改过的对象。
为什么要堆?