如何使用智能指针跟踪类的对象

How do I track objects of a class using smart pointers?

本文关键字:对象 跟踪 指针 何使用 智能      更新时间:2023-10-16

我正在尝试编写一个对象相互识别的类(即具有指向所有对象的指针)。我无法理解有关智能指针和静态成员的这个想法的实现的某些方面。

可以将该类视为需要能够访问其他游戏对象的成员函数和属性的游戏对象。

据我所知,实现所需设计的常用方法是静态向量,它将包含指向其他对象的指针。如果使用原始指针进行操作,则任务不是很复杂:

游戏对象.h:

#pragma once
#include <vector>    
class GameObject
{
private:
    static std::vector< GameObject* > objects;
public:
    GameObject()
    {
        objects.push_back(this);
    }
};

游戏对象.cpp:

#include "GameObject.h"
std::vector< GameObject* > GameObject::objects = {};

这实际上会提供我需要的东西。但是,如果我想使用智能指针,事情对我来说就不那么简单了。从这个问题和迈耶斯的《有效的现代C++》一书中,我发现了std::enable_shared_from_thisshared_from_this()。但是,此外,参考明确指出,只有在对象已经归std::shared_ptr<>所有的情况下,才允许使用shared_from_this()

因此,不可能像以前一样简单地将构造函数中的指针this(或在其上构造std::shared_ptr)推入静态向量。我发现的允许设计的最小代码集如下:

游戏对象.h:

#pragma once
#include <vector>
#include <memory>

class GameObject : public std::enable_shared_from_this<GameObject>
{
private:
    static std::vector< std::shared_ptr<GameObject> > objects;
public:
    GameObject() {}
    void emplace_ptr()
    {
        objects.emplace_back(shared_from_this());
    }
};

游戏对象.cpp:

#include "GameObject.h"
std::vector< std::shared_ptr<GameObject> > GameObject::objects = {};

主.cpp:

#include "GameObject.h"
int main(int argc, char* argv[])
{
    std::shared_ptr<GameObject> game_object{ new GameObject{} };
    game_object->emplace_ptr();
    return 0;
}

所以我显然有义务创建一个指向外部某处对象的指针,然后显式调用一个方法来将指针推送到静态向量(因为我不允许在构造函数中这样做)。

我的印象是所需的代码变得不必要地复杂(比较原始指针大小写)或者我正在做一些荒谬的事情。

  1. 我努力使对象相互感知是否有意义完全?这是一个常见问题还是其他方法通常是采取?
  2. 静态向量是解决问题的合理解决方案吗?
  3. 如何使用智能指针构造这样的向量,但最好是无需外部干预,也无需创建特殊功能emplace_ptr()为目的?

你不需要enable_shared_from_this,相反,你可以有一个static工厂函数来创建(共享)实例,将其放入向量中,并返回它。

类似的东西

class GameObject
{
private:
    static std::vector<std::shared_ptr<GameObject>> objects;
    // Don't allow creation from outside
    GameObject() {}
public:
    static std::shared_ptr<GameObject> create()
    {
        objects.emplace_back(new GameObject);
        return objects.back();
    }
};

然后要获得一个实例,例如

auto new_game_object = GameObject::create();

这只有一个问题:共享指针指向的对象只要在向量中,就永远不会超出范围,而向量的生存期就是程序的生存期(因为它是static的)。因此,您必须考虑何时何地从向量中删除这些实例。

我会首先将静态向量移动到游戏对象类之外,并避免将管理游戏对象的代码放在游戏对象本身中。其他用户提出的静态create()方法会有所帮助,但我个人更喜欢自己调用GameObject/Derived game对象构造函数的能力,这样我就可以在构造过程中传递我需要的任何数据。

创建类似 GameObjectWorld 类的内容,您可以在游戏引擎执行期间向其添加对象。无需直接从游戏对象构造函数自动将它们添加到世界中。

您可以执行以下操作:

auto gameObject = std::make_shared<GameObject>();
// implemented at a singleton for simplicity
GameObjectWorld::getInstance().add(gameObject);

过去,我采用的方法是,给定的游戏"部分"或"场景"维护自己的游戏对象(实体)列表,而游戏对象(实体)又维护自己的列表。这允许您通过递归来执行操作,并具有对象层次结构的额外好处。

这是一个粗略而简单的例子:

class Part
{
    std::vector<std::shared_ptr<GameObject> children;
public:
    void addChild(std::shared_ptr<GameObject> object)
    {
        children.push_back(object);
    }
    // Part lifecycle
    virtual void onCreate() = 0; // must be provided by your parts
};
class GamePart :: public Part
{
    void onCreate() override; // called by your engine
};
void GamePart::onCreate()
{
    addChild(std::make_shared<GameObject>());
}

这是开始管理游戏对象的一种非常简单的方法,我强烈建议您阅读"实体组件系统"并了解行业专家管理其游戏世界的一些方式。