C++ 喜欢堆而不是堆?

C++ 喜欢堆而不是堆?,c++,class,object,C++,Class,Object,我最近投身于图形编程,我注意到许多图形引擎(即Ogre)和许多编码人员都喜欢动态初始化类实例。这里有一个例子 ogreHead和headNode数据成员和方法称为ogreHead->blablabla 为什么要乱搞对象指针而不是普通对象 顺便说一句,我还读到过堆内存分配比堆栈内存分配慢得多。堆栈的范围是有限的:它只存在于函数中。现在,现代的用户接口程序通常是事件驱动的,这意味着调用您的函数来处理事件,然后该函数必须返回才能使程序继续运行。因此,如果事件处理程序函数希望创建一个在函数返回后仍然存在

我最近投身于图形编程,我注意到许多图形引擎(即Ogre)和许多编码人员都喜欢动态初始化类实例。这里有一个例子

ogreHead
headNode
数据成员和方法称为
ogreHead->blablabla

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


顺便说一句,我还读到过堆内存分配比堆栈内存分配慢得多。

堆栈的范围是有限的:它只存在于函数中。现在,现代的用户接口程序通常是事件驱动的,这意味着调用您的函数来处理事件,然后该函数必须返回才能使程序继续运行。因此,如果事件处理程序函数希望创建一个在函数返回后仍然存在的对象,那么很明显,该对象无法分配到该函数的堆栈上,因为一旦函数返回,该对象将不再存在。这就是我们在堆上分配东西的主要原因

还有其他原因

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


此外,通常使用工厂方法,如
where::createEntity()
。如果必须调用单独的方法来创建对象,则无法在堆栈上创建该对象,原因在本答案的第一段中解释。

堆分配不可避免地比堆栈分配慢得多。更多关于“慢了多少?”的信息,请稍后。然而,在许多情况下,选择是“为您而做”,原因有几个:

  • 堆栈是有限的。如果你用完了,应用程序几乎总是会被终止-没有真正好的恢复,甚至打印一条错误消息说“我用完了堆栈”可能很难
  • 当您离开进行分配的函数时,堆栈分配“消失”
  • 可变性的定义更明确,也更容易处理。C++不很好地处理“可变长度数组”,而且它肯定不能保证在所有编译器中都能工作。<李> 堆比堆栈慢多少

    我们将在一段时间内讨论“这是否重要”

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

    然而,通过一些相当聪明的编程,这一点基本上是隐藏的——例如,在对象的生命周期中,分配一些长时间处于分配状态的内容将“不需要担心”。在3D游戏中,从堆中为每个像素或每个试验分配对象显然是个坏主意。但如果对象的生命周期为多帧甚至整个游戏,则分配和释放对象的时间几乎为零

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

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

    对我来说,规则是:

  • 它能很好地放在堆栈上吗?通常几千字节就可以了,很多千字节不太好,而兆字节肯定不行
  • 是否已知数量(例如对象数组)以及最大值,以便您可以将其放入堆栈中
  • 您知道对象将是什么吗?换句话说,抽象/多态类可能需要在堆上分配
  • 它的生存期是否与它所在的作用域相同?如果不相同,请使用堆(或者向下堆栈,并将其向上传递)

  • [1] 或者,如果堆栈“向高地址增长”-我不知道有哪台机器有这样的体系结构,但这是可以想象的,我认为已经有了一些。C当然没有承诺堆栈以何种方式增长,也没有承诺运行时堆栈如何工作。

    为什么使用指针而不是对象

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

    shoot(Orge::Entity ogre)
    
    而不是

    shoot(Orge::Entity* ogrePtr)
    
    如果ogre不是指针,那么发生的事情是将整个对象传递到函数中,而不是引用。如果编译器没有优化,则留下的程序效率低下。还有其他原因,使用指针,您可以修改传入的对象(有些人认为引用更好,但这是另一种讨论)。否则,您将花费太多时间来回复制修改过的对象

    为什么堆

  • 从某种意义上说,堆是一种更安全的内存访问类型,允许您安全地重置/恢复。如果您调用new并且没有内存,您可以将其标记为错误。如果您正在使用堆栈,实际上没有其他方法可以知道您导致了堆栈溢出
    shoot(Orge::Entity* ogrePtr)