Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize a class members reference variable, as if it was a regular variable

Right now, I am writing a physics engine for a game I am developing. Often times, there is many repeated values when you combine a physics engine with a game engine. Such as variables denoting the position and rotation of an object. With most physics engines, you have to iterate through all the objects and update their positions according to the physics engine's object positions. So I thought it would be desirable to have the position and rotational values in the physic engines objects to be references to the game engines object's variables handling rotation and position. However, sometimes you want objects in the physics engine that don't correlate directly with objects in the game engine. (Invisible walls, joints). So you would need to treat the objects as regular member variables... So here is what I have.

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

However, this will lead to memory leaks, as those floats are not getting deleted. Do you have any suggestions about how I can achieve the desired behavior without the memory leak?

EDIT

I would rather not use boost. However I am not opposed to a solution that requires a template. Also, with this partially being a performance optimization, boost::shared_ptr, does not seem to be the right solution.

like image 730
Skyler Saleh Avatar asked Feb 26 '11 17:02

Skyler Saleh


People also ask

How do you initialize a reference variable?

There are three steps to initializing a reference variable from scratch: declaring the reference variable; using the new operator to build an object and create a reference to the object; and. storing the reference in the variable.

How do you initialize const and reference member variables?

To initialize the const value using constructor, we have to use the initialize list. This initializer list is used to initialize the data member of a class. The list of members, that will be initialized, will be present after the constructor after colon. members will be separated using comma.


3 Answers

I would suggest you use a boost::shared_ptr to these position structures. This way, you don't have to worry about deletion and you can use it as either a pointer that is shared with the game engine object or as a stand-alone pointer.

Since there is overhead to these, you might want to limit the data-to-pointer ratio. In other words, don't keep a shared_ptr for each coordinate, but a shared_ptr to the position vector and a shared_ptr to the rotation rep., or, a shared_ptr to a homogeneous transform or frame (coordinate system, kinetic frame, or kinetostatic frame).

For example, you could have this:

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

The automatic and reference counting property of the shared_ptr will make it so that you won't need to worry about putting a delete statement (automatic) and there is no danger of the object disappearing from the game engine while the physics engine still needs those variables (reference counting guarantees that they will be deleted only when all objects that need them are deleted as well).

EDIT

I would rather not use boost. However I am not opposed to a solution that requires a template. Also, with this partially being a performance optimization, boost::shared_ptr, does not seem to be the right solution.

Well, shared_ptr/shared_array can also be found in the standard library technical report 1 (TR1) (so it's std::tr1::shared_ptr instead, so you don't need to use Boost to use those). As for performance optimization, that is why I recommend a fairly high ratio of data-to-pointer. The overhead of shared_ptr is mainly a memory overhead and some indirection during deletion and copying (which are two operations that are not done so often), I don't think there is much overhead in accessing the data it points to as compared to a regular pointer or a reference. You have to accept that, even by using references, you are trading data-copying overhead with data-access indirection overhead (you are also sacrificing memory locality, which is a big deal!). I would say that the performance drop related to memory locality being broken is going to be far worse than just the indirection alone. So, when it comes to accessing elements, IMO, shared_ptr, raw pointers and references will have very little performance difference. In many algorithms that uses these shared variables, you would probably be better off copying the data pointed to by the pointer/reference to local variables, calculate with and on those local variables, and then copying them back to the memory pointed to by the pointer/reference.

I recommend that you do some tests of your own on the performance when using either solutions (using shared_ptr, using references or raw pointers, and copying the data between the game engine and physics engine) and see for yourself, you might be surprised at what you find.

EDIT2

Have you considered using multiple inheritance scheme. This problem can probably be very well served with a diamond inheritance scheme:

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 ...
};

This above schemes make the physics object and the game-engine object share the same position data (with little indirection, little memory-overhead and little memory-locality problems). I'm pretty sure this would be more efficient than any other scheme, but arguments about performance can only be answered with test results that you will have to do on your own if performance is really your prime concern (make sure that you are not doing premature optimization!).

like image 105
Mikael Persson Avatar answered Oct 03 '22 01:10

Mikael Persson


You could use Boost shared_array to share the XYZ coordinates among an arbitrary number of objects:

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

The shared_array and shared_ptr templates employ reference counting to ensure that the memory is deleted after the last reference to it is destroyed. Copy-constructing a shared_array or shared_ptr adds one to the reference count and destroying a shared_array or shared_ptr subtracts one from the reference count. When the reference count reaches 0, the shared memory is deleted.

like image 44
Daniel Trebbien Avatar answered Oct 03 '22 00:10

Daniel Trebbien


Do what you're doing now, just keep an extra bool variable to indicate whether your memory was allocated or not. Then, in the destructor, you can call delete after checking that value, i.e.

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;
};
like image 30
Benjamin Lindley Avatar answered Oct 02 '22 23:10

Benjamin Lindley