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

时间:2016-07-06 17:53:34

标签: c++ c++11 pointers static smart-pointers

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

该类可以被认为是一个游戏对象,需要能够访问其他游戏对象的成员函数和属性。

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

GameObject.h:

#pragma once

#include <vector>    

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

GameObject.cpp:

#include "GameObject.h"

std::vector< GameObject* > GameObject::objects = {};

这实际上可以满足我的需要。但是,如果我想使用智能指针,事情对我来说并不那么简单。从this question和Meyers的“Effective Modern C ++”一书中我发现了std::enable_shared_from_thisshared_from_this()。但是,另外,the reference明确指出,shared_from_this()仅在对象已由std::shared_ptr<>拥有的情况下才被允许使用。

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

GameObject.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());
    }
};

GameObject.cpp:

#include "GameObject.h"

std::vector< std::shared_ptr<GameObject> > GameObject::objects = {};

main.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()是为了这个目的吗?

2 个答案:

答案 0 :(得分:1)

您不需要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();

这只有一个问题:共享指针指向的对象永远不会超出范围,只要它们在向量中,并且向量的生命周期是程序的生命周期(因为它是{ {1}})。因此,您必须考虑从向量中删除这些实例的位置和时间。

答案 1 :(得分:0)

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

创建类似GameObjectWorld类的东西,您可以在游戏引擎执行期间添加对象。没有必要直接从GameObject构造函数中自动将它们添加到世界中。

您可以执行以下操作:

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

在过去,我采用的方法是给定游戏“Part”或“Scene”维护它自己的GameObjects(实体)列表,而后者又维护自己的列表。这允许您通过递归来执行操作,并具有对象层次结构的额外好处。

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

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>());
}

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