以优雅的方式管理指向游戏引擎中已销毁对象的指针
Managing pointers to destroyed objects in a game engine in an elegant way
我目前正在构建一个游戏引擎,运行遇到了一些问题。我已经找到了几个解决方案,但它和我希望的一样优雅。
这是问题所在:
- 在引擎中实例化对象A
- 获取指向对象 A 的指针 - ObjectA*
- 销毁对象 A
- ObjectA* 变成悬空指针
使用 shared_ptr 是不行的,因为它可以锁定我们不想要的对象。对象A被销毁没有错,我只是需要一种方法来检查它是否被销毁。
这个问题我有两个解决方案:
第一种方法是 return 一个代理对象,只要你想引用一个对象。对于我们确定该对象有效的情况,该代理对象可以隐式转换为原始指针。在我们将引用保留更长时间的情况下,我们会使用代理对象,它基本上只是一个指向指针的指针。当对象被销毁时,我们只需将它的指针设置为nullptr
,然后代理就可以检查
第二种解决方案是完全不使用 return 指针。相反,每当我们需要一个对象的引用时,我们都会传递我们希望将其分配给的指针作为参数。然后引擎会将所述指针保存在内存中,并在对象被销毁时手动将其设置为 nullptr
。
以下是我首选的优雅解决方案的要求:
- 只使用原始指针作为引用
- 所述原始指针 return 从函数调用中编辑(即
Ptr* AddCompoenent<>()
而不是 void AddComponent<>(Ptr*&)
- 当它们指向的对象被销毁时,指针将变为
nullptr
这可能吗?
如果你想 return 简单的指针,当你释放它时没有办法收集所有指向对象的指针并将它们设置为 nullptr
如果你只是使用普通的 C++ 对象。
当然有一个解决方案,它不是新的,它被称为垃圾收集。这正是您需要执行的算法;它分析堆栈和堆,并能够收集所有指向您的对象的指针。如果对象找到指向它的指针,而不是 保留 对象,您希望它将指针设置为 nullptr
.
现在垃圾回收有几个先决条件。一是你必须能够在运行时识别堆栈和堆上的所有指针值,这样你就可以确定一些数据实际上是指向你的对象的指针,而不仅仅是一些整数或其他恰好包含一个值的值看起来像指向您的对象的指针。如果您仍想在代码中使用简单的指针类型,则需要编译器的支持才能在运行时获得该信息——而 C++ 不提供。这就是为什么垃圾收集通常是为整个语言定义的。
此外,使用此算法对性能的影响是可怕的:每次释放对象时,都需要分析所有堆栈和堆。这意味着它比普通的基于 gc 的语言运行它的频率要高得多,看看它们因此而造成的性能损失。
我目前正在构建一个游戏引擎,运行遇到了一些问题。我已经找到了几个解决方案,但它和我希望的一样优雅。
这是问题所在:
- 在引擎中实例化对象A
- 获取指向对象 A 的指针 - ObjectA*
- 销毁对象 A
- ObjectA* 变成悬空指针
使用 shared_ptr 是不行的,因为它可以锁定我们不想要的对象。对象A被销毁没有错,我只是需要一种方法来检查它是否被销毁。
这个问题我有两个解决方案:
第一种方法是 return 一个代理对象,只要你想引用一个对象。对于我们确定该对象有效的情况,该代理对象可以隐式转换为原始指针。在我们将引用保留更长时间的情况下,我们会使用代理对象,它基本上只是一个指向指针的指针。当对象被销毁时,我们只需将它的指针设置为nullptr
,然后代理就可以检查
第二种解决方案是完全不使用 return 指针。相反,每当我们需要一个对象的引用时,我们都会传递我们希望将其分配给的指针作为参数。然后引擎会将所述指针保存在内存中,并在对象被销毁时手动将其设置为 nullptr
。
以下是我首选的优雅解决方案的要求:
- 只使用原始指针作为引用
- 所述原始指针 return 从函数调用中编辑(即
Ptr* AddCompoenent<>()
而不是void AddComponent<>(Ptr*&)
- 当它们指向的对象被销毁时,指针将变为
nullptr
这可能吗?
如果你想 return 简单的指针,当你释放它时没有办法收集所有指向对象的指针并将它们设置为 nullptr
如果你只是使用普通的 C++ 对象。
当然有一个解决方案,它不是新的,它被称为垃圾收集。这正是您需要执行的算法;它分析堆栈和堆,并能够收集所有指向您的对象的指针。如果对象找到指向它的指针,而不是 保留 对象,您希望它将指针设置为 nullptr
.
现在垃圾回收有几个先决条件。一是你必须能够在运行时识别堆栈和堆上的所有指针值,这样你就可以确定一些数据实际上是指向你的对象的指针,而不仅仅是一些整数或其他恰好包含一个值的值看起来像指向您的对象的指针。如果您仍想在代码中使用简单的指针类型,则需要编译器的支持才能在运行时获得该信息——而 C++ 不提供。这就是为什么垃圾收集通常是为整个语言定义的。
此外,使用此算法对性能的影响是可怕的:每次释放对象时,都需要分析所有堆栈和堆。这意味着它比普通的基于 gc 的语言运行它的频率要高得多,看看它们因此而造成的性能损失。