C++ 快速实例化对象-好还是坏?

C++ 快速实例化对象-好还是坏?,c++,oop,C++,Oop,注意:虽然这个问题与游戏没有直接关系,但我已经塑造了游戏开发的背景,以便更好地可视化与这个问题相关的场景 tl;dr:正在快速创建同一类的对象,这是内存密集型、效率低下还是常见做法 假设我们有一个“bullet”类,并且每次玩家“射击”时都会创建一个该类的实例——每秒1到10次。这些实例(显然)可能在碰撞时被破坏 这是个糟糕的主意吗?这里的通用OOP(即:类项目符号{short x;short y;}等)可以吗?还是有更好的方法?是否首选新建和删除 非常感谢您的任何意见。谢谢大家! 想想,在每次

注意:虽然这个问题与游戏没有直接关系,但我已经塑造了游戏开发的背景,以便更好地可视化与这个问题相关的场景

tl;dr:正在快速创建同一类的对象,这是内存密集型、效率低下还是常见做法

假设我们有一个“bullet”类,并且每次玩家“射击”时都会创建一个该类的实例——每秒1到10次。这些实例(显然)可能在碰撞时被破坏

这是个糟糕的主意吗?这里的通用OOP(即:
类项目符号{short x;short y;}
等)可以吗?还是有更好的方法?是否首选
新建
删除


非常感谢您的任何意见。谢谢大家!

想想,在每次实例化时,堆中都有一个地方需要分配内存并创建一个新实例。这可能会影响性能。尝试使用collection并创建一个包含所需项目符号数的集合实例。

不要只是不断创建和删除对象。相反,另一种选择是拥有一个可以重用的常量、可调整大小的数组或对象实例列表。例如,创建一个包含100个项目符号的数组,它们不必全部绘制,而是有一个
布尔值
,说明它们是否处于“活动”状态

然后,每当您需要一个新的项目符号时,“激活”一个不活动的项目符号,并将其位置设置在您需要的位置。然后,每当它离开屏幕时,您可以再次将其标记为非活动状态,而不必删除它

如果您需要超过100颗子弹,只需扩展阵列即可


请考虑阅读本文以了解更多信息:。它还有其他几个与游戏模式相关的主题。

这听起来像是一个很好的使用案例,适用于或之类的技术。这两种方法的思想都是为预先分配的一定数量的元素提供内存。您可以重写类的
new
操作符以使用池/列表,或者使用
placement new
在检索到的地址中实例化类

优点:

  • 没有内存碎片
  • 很快
缺点是:

  • 您必须事先知道元素的最大数量

    • 分配对象时发生的最起码的事情是函数调用(其构造函数)。如果这种分配是动态的,那么内存管理的成本也会随着碎片的出现而急剧增加

      每秒调用某个函数10次真的很糟糕吗?不会。每秒10次动态创建和销毁许多小对象是否会很糟糕?可能地你应该这样做吗?绝对不是

      即使没有“感觉到”性能损失,但当一个最佳解决方案立即可用时,拥有一个次优解决方案也是不好的


      因此,与动态添加和删除对象的
      std::list
      不同,您可以简单地使用项目符号的
      std::vector
      ,其中项目符号的添加意味着附加到向量(在向量达到足够大的大小后,应该不再需要任何内存分配)删除意味着将被删除的元素与最后一个元素交换,并将其从向量中弹出(实际上只是减少向量大小变量)。

      有很多方法。堆栈上的分配比堆上的快,如果需要heep,可以使用
      std::vector::reserve
      ,这完全取决于如何实例化它们,@delnan?最好的方法是什么?这取决于如何使用它们、需要什么以及可以做哪些权衡……;-)真的,没有一个最好的方法。我担心这个问题太宽泛了。谢谢你的文章!在大多数实现中,当您“激活”一个对象时,您实际上是在实例化一个对象。你不分配内存,如果这就是你所说的。@delnan不,我的意思是,如果你愿意,你可以懒洋洋地初始化前100个,或者如果你想提前支付成本,你可以主动启动它们。但是,无论何时使用完它们,都不要删除它们,只需清空它们的值(或者只为渲染器将它们标记为非活动),无论何时在用完空项目符号后需要新对象,都要重置最旧的非活动项目符号的参数(或重新激活方案的参数)。这也有助于碎片化。@zero298我完全明白你的意思。但是“重置参数”实际上是一个构造函数调用(事实上,许多池实现就是这样做的,它们在删除对象时调用析构函数,在请求新对象时调用placement
      new
      )。你刚刚给自己省了一笔钱。@delnan啊,我不知道。谢谢你有什么文章可以让我读得更多吗?这会比使用stack更有效吗?你说的“使用stack”是什么意思?@Adosi很难回答。自动存储持续时间(您可能指的是“堆栈”)允许进行更多优化,例如将对象拆分为寄存器(如果对象足够小,并且您不获取其地址,并且满足一些其他条件)。另一方面,在一个设计合理的池/空闲列表中的分配实际上与分配堆栈内存一样便宜,而且内存也可能在缓存中。只有当您将“实例化”理解为“调用
      new Bullet
      ”时,这才是创建对象的唯一方法。这听起来非常实用。非常感谢。注意,有时构造函数可以内联。还有更好的方法可以重新使用删除项目符号的空间(例如,交换和弹出可以避免创建时的线性搜索)。@delnan,true。我换成那个。