2

Consider the following example

class GameObject {
  vec3 position;
  SphereCollider collider;
  // ...
};

SphereCollider* GameObject::getCollider(){
  return &collider;
}


class SphereCollider {
  vec3 position;
  float radius;
};

You can notice that in this design there is a duplicity of a member variable position shared among more classes. The radius is there to show there can be other data members.

The SphereCollider is later used for collision detection and I have more classes of GameObject (with no common ancestor) which compose the SphereCollider.

What is the cleanest way to handle this situation design and performance wise? Is there a convention in this?

There are few solutions in my mind, but none of them is perfect. Can you think of a better solution?

1) Persistent data member access via getter/setter

Cons: No direct access to the data member inside the class. Is there a convention to tell this truth to the programmer who will further work on this class?

void GameObject::setPosition(vec3& pos){
  position = pos;
  colliser->setPosition(pos);
}

vec3 GameObject::getPosition(){
  return position;
}

2) Remove redundancy and use getter/setter access to the SphereCollider

Cons: similar to 1)

class GameObject {
  SphereCollider collider;
};

void GameObject::setPosition(vec3& pos){
  colliser->setPosition(pos);
}

3) Reference/pointer from SphereCollider to GameObject data members

Cons: Reallocating (e.g. std::vector) the GameObject may cause troubles. Is there a way of telling the user of this class?

class GameObject {
  vec3 position;
  SphereCollider collider;
public:
  GameObject(){
    collider = SphereCollider(position);
  }
};

class SphereCollider {
  const vec3& position;
  float radius;
public:
  Collider(vec3& pos)
    : position(pos)
  {
  }
};

4) Propagate data to the SphereCollider on demand

Cons: User can store the reference to the collider an skip using getCollider();

SphereCollider* GameObject::getCollider(){
  collider->setPosition(position);
  return &collider;
}
Bublafus
  • 191
  • 6

1 Answers1

0

Said that the particular choice may depend on various more general design factors, I would prefer your option (3). You can easily solve the problem with vector resizing by defining the proper move constructor of GameObject: it will be invoked when needed.

See these question/answers for example:

Why does resize() cause a copy, rather than a move, of a vector's content when capacity is exceeded?

How to enforce move semantics when a vector grows?

Community
  • 1
  • 1
Sigi
  • 4,826
  • 1
  • 19
  • 23