初始化类成员引用变量,就像它是常规变量一样

时间:2011-02-26 17:39:49

标签: c++ variables reference

现在,我正在为我正在开发的游戏编写物理引擎。通常,当您将物理引擎与游戏引擎结合使用时,会有许多重复值。例如表示对象的位置和旋转的变量。对于大多数物理引擎,您必须遍历所有对象并根据物理引擎的对象位置更新其位置。所以我认为最好让物理引擎对象中的位置和旋转值引用游戏引擎对象处理旋转和位置的变量。但是,有时您希望物理引擎中的对象不直接与游戏引擎中的对象相关联。 (隐形墙,关节)。所以你需要将对象视为常规成员变量...... 所以这就是我所拥有的。

struct object{
  float & xPosition;
  float & yPosition;
  float & zPosition;
  ...
  object(float & xPos, float& yPos, float& zPos):xPosition(xPos), yPosition(yPos), zPosition(zPos){}
  object():xPosition(*new float(0.0f)), yPosition(*new float(0.0f)), zPosition(*new float(0.0f)){}
};

然而,这会导致内存泄漏,因为这些浮动没有被删除。对于如何在没有内存泄漏的情况下实现所需的行为,您有什么建议吗?

修改

我宁愿不使用boost。但是,我并不反对需要模板的解决方案。此外,这部分是性能优化,boost :: shared_ptr,似乎不是正确的解决方案。

5 个答案:

答案 0 :(得分:5)

我建议您对这些位置结构使用boost::shared_ptr。这样,您不必担心删除,可以将其用作与游戏引擎对象共享的指针或作为独立指针使用。

由于存在开销,您可能希望限制数据与指针的比率。换句话说,不要为每个坐标保留shared_ptr,而要为位置向量保留shared_ptr,为轮换代表保留shared_ptr,或者shared_ptr到均匀变换或框架(坐标系,动力学框架或动力学框架)。

例如,你可以这样:

class object {
  public:
    typedef boost::shared_ptr<Vector3D> pVector3D;
  private:
    pVector3D position;
  public:
    object(pVector3D aPos = pVector3D(new Vector3D(0.0,0.0,0.0))) : position(aPos) { };
};

shared_ptr的自动和引用计数属性将使您不必担心放置删除语句(自动),并且物理引擎仍然没有对象从游戏引擎中消失的危险需要这些变量(引用计数保证只有在删除了所有需要它们的对象时才会删除它们。)

修改

  

我宁愿不使用boost。然而   我并不反对这样的解决方案   需要一个模板。还有,这个   部分是表演   优化,boost :: shared_ptr,确实如此   似乎不是正确的解决方案。

好吧,shared_ptr / shared_array也可以在标准库技术报告1(TR1)中找到(所以它是std::tr1::shared_ptr,所以你不需要使用Boost来使用那些)。至于性能优化,这就是为什么我建议使用相当高的数据到指针比例。 shared_ptr的开销主要是内存开销和删除和复制期间的一些间接(这是两个经常没有完成的操作),我认为与常规数据相比,访问它指向的数据的开销并不大。指针或引用。你必须接受这一点,即使使用引用,你也在交换带有数据访问间接开销的数据复制开销(你也牺牲了内存局部性,这是一个大问题!)。我会说,与内存局部性相关的性能下降将远远超过单独的间接性。因此,当涉及访问元素时,IMO,shared_ptr,原始指针和引用将具有非常小的性能差异。在许多使用这些共享变量的算法中,最好将指针/引用指向的数据复制到局部变量,用这些局部变量计算,然后将它们复制回指针所指向的内存中。 /参考。

我建议您在使用任一解决方案(使用shared_ptr,使用引用或原始指针,以及在游戏引擎和物理引擎之间复制数据)时对性能进行一些测试,并亲眼看看,您可能是对你所发现的东西感到惊讶。

<强> EDIT2

您是否考虑过使用多重继承方案?钻石继承方案可能很好地解决了这个问题:

class PositionedObject {
  protected:
    float Position[3];
  public:
    PositionedObject(float x,float y, float z) { Position[0] = x; ... };
    virtual ~PositionedObject() { };
};

class VisibleObject : virtual public PositionedObject { //note that the "virtual" keyword is critical here.
  ... rendering-related code ... i.e. the game-engine side of the implementation
};

class RigidBody : virtual public PositionedObject { //again "virtual" is very important.
  ... physics code here ...
};

class MyObject : public VisibleObject, public RigidBody {
  ... code specific to MyObject ...
};

上述方案使物理对象和游戏引擎对象共享相同的位置数据(间接很少,内存开销很小,内存局部性问题很少)。我很确定这比其他任何方案都更有效率,但关于性能的争论只能通过你必须自己做的测试结果来回答,如果性能确实是你最关心的问题(确保你没有做到)过早优化!)。

答案 1 :(得分:3)

您可以使用Boost shared_array在任意数量的对象之间共享XYZ坐标:

struct object {
    boost::shared_array<float> coords;

    object(const boost::shared_array<float>& coords_)
        : coords(coords_)
    {
    }

    object()
        : coords(new float[3])
    {
        coords[0] = coords[1] = coords[2] = 0.f;
    }
}

shared_arrayshared_ptr模板使用reference counting来确保在最后一次引用内存被删除后删除内存。复制构建shared_arrayshared_ptr会在引用计数中添加一个并销毁shared_arrayshared_ptr从引用计数中减去一个。当引用计数达到0时,将删除共享内存。

答案 2 :(得分:1)

一种非常简单的方式......

struct object
{
    float& xPosition;
    float& yPosition;
    float& zPosition;
    float x, y, z;
    ...

    object(float& xPos, float& yPos, float& zPos)
      : xPosition(xPos), yPosition(yPos), zPosition(zPos)
    { }

    object() : xPosition(&x_), yPosition(&y_), zPosition(&z_), x(0), y(0), z(0)
    { }
};

...当任何给定对象只需要一个时,你最终会将引用和变量减半,但是管理其他地方的麻烦和开销可能很容易花费更多(无论是在内存还是代码膨胀来自模板实例化)。

如果您确实希望坚持更接近自己的东西,可以简单地添加一个布尔值来跟踪是否需要删除内存。 (编辑:PigBen刚刚发布了这样的代码)。就个人而言,我建议使用一个new float[3],这样你就可以进行一次内存分配而不是三次...不仅会更快,而且在堆管理中也可能浪费更少的内存。

答案 3 :(得分:1)

执行您现在正在做的事情,只需保留一个额外的bool变量来指示您的内存是否已分配。然后,在析构函数中,您可以在检查该值后调用delete,即

struct object{
  float & xPosition;
  float & yPosition;
  float & zPosition;

  object(float & xPos, float& yPos, float& zPos)
    :xPosition(xPos),
     yPosition(yPos),
     zPosition(zPos),
     allocated(false)
  {}
  object()
    :xPosition(*new float(0.0f)),
     yPosition(*new float(0.0f)),
     zPosition(*new float(0.0f)),
     allocated(true)
  {}

  ~object() {
    if(allocated) {
      delete &xPosition;
      delete &yPosition;
      delete &zPosition;
    }
  }

private:
  bool allocated;
};

答案 4 :(得分:0)

用于指针的定制簿记系统是合适的。您可以将坐标作为指针传递给物理系统,如果物理引擎应将它们添加到簿记结构中,则使用布尔值指定 - 指针列表将是最简单的选择。然后,可以通过迭代列表来释放为物理引擎动态保留的所有内存。或者,您可以处理每个对象中的内存,但这样做的缺点是将大部分不必要的数据添加到用于执行实际计算的内存区域。当然,如果您需要管理每个对象的内存而不是每个级别,那么向对象添加数据可能是唯一合理的解决方案。在这种方法中,释放/保留操作更复杂,但是在引擎运行时节省了一些处理。这是一个在对象中只有一个浮点指针的解决方案,它指向一个三浮点数组:

class physics_system_t {
public:
    std::vector<float *> my_arrays;
} physics_system;

class object {
public:
    object(float * coords_ptr, bool manage_memory) : coords_array(coords_ptr)
    {
        if (manage_memory) {
            physics_system.my_arrays.push_back(coords_ptr);
        }
    }
    float *coords_array;
};

编辑:这是另一个尚未提出的替代方案。使用union将数据存储为局部变量或引用,并使用布尔值在两者之间切换。

class object {
public:
    object(float & x, float& y, float& z, bool use_refs);
    union {
        struct refs_t {
            float &xr;
            float &yr;
            float &zr;
        } refs;
        struct vals_t {
            float x;
            float y;
            float z;
        } vals;
    }
    bool use_refs;
};

当内存由对象处理时,这将保留数据局部性,不需要额外的空间,并且您不需要进行任何手动删除。当然,性能可能仍然是一个问题,因为每次访问时都需要选择是使用引用还是变量。您还需要小心,因为您不想在意图使用变量时意外使用引用。增益可能不值得这个问题和增加的代码大小。