2

I have a quick question.

I started learning C++ a while ago and have always used vectors with shared pointers of objects in them. Or at least smart pointers.

I'm currently programming a game and I was thinking about why I would use smart pointers.

Assume I have a Game class and it has a vector of players.

When would I want to choose for vector<Player> players and when would i want to choose for vector<shared_ptr<Player>>?

6
  • 3
    Four obvious cases are 1) Sharing objects between two vectors (as distinct from two vectors that contain copies of each other's objects) (2) a vector of polymorphic objects. (3) cases where the lifetime of an object may need to extend beyond lifetime of the vector that refers to it. (4) Combinations of 1-3. Commented Jan 2, 2018 at 11:49
  • @Peter for (2) and (3) I'd prefer unique_ptr. shared_ptr is very niche when used appropriately Commented Jan 2, 2018 at 12:00
  • (5) When the object is sufficiently big/expensive that you don't want to copy-and-destroy on vector resize or element erasure (only if the object isn't moveable and you actually need to dynamically resize or rearrange your vector) Commented Jan 2, 2018 at 12:09
  • @Caleth - Sure. However, the question concerned use of shared_ptr. I listed cases in which I would consider using shared_ptr. That's the problem with generic questions like this - there are lots of considerations in deciding which option to use, not absolute "thou shalt" type rules. Commented Jan 2, 2018 at 12:34
  • @Useless why would you want an object that is copyable but not movable? Commented Jan 2, 2018 at 12:37

1 Answer 1

2
  1. Use shared_ptrs if the state of the entities is accessed other than in the vector. Also if you make an event-loop, with events like "player collided with other entity". You need to copy a pointer/reference, else you cannot change the state of the entities.

  2. Another important thing is when the entity dies but an event with the entity is not handled, so the entity is deleted from the vector. When you use shared_ptr, the entity lives further, until the event in the eventqueue is deleted. When you store the whole player in the vector, the pointer stored in the event-object is not pointing to the correct entity anymore, if the vector changes its size. When you use a shared_ptr, the entity can be used normally, even if it is not existing in the vector anymore. But it is better to use std::weak_ptr in the event, that the entity is deleted, when it is dead, weak_ptr can check if the pointer is a dangling pointer which means the entity is dead and no event needs to be handled.

  3. Using levels/dimensions, in which the vector of entities lives: When some entity goes to another level, the entity needs to be moved to that level, copying the whole struct is not the best idea. So the best idea is to use some pointer type like shared_ptr, but I recommend to std::move the shared_ptr to reduce reference counting overhead. With std::move the shared_ptr is as fast as a raw ptr.

  4. Virtual classes of entities: When you are writing a game in a 2D or 3D world, you want to store many entities of different kind in the vector, you must use some pointertype to refer to the object, because the entities may have different size.

  5. Fixed player count: If you are programming a game with fixed count of players like a card-game, you do not move the players nor you delete one (however you can just set a flag to indicate that the player is dead and you do not need to erase the entity from the vector), the best way is to use std::vector<Player>.

I think, using shared_ptr living in the vector and with weak_ptrs referencing in events is the best way to manage entities. While weak_ptr(the faster way) does not keep the memory of the Entity alive, the pointer needs to be checked every time.

Some pseudo code

if(fixed count of players && there are no other entities && there are no virtual subclasses of Player) { class Player { ... }; using player_collection = std::vector<Player>; using reference_to_player = Player*; } else { class Entity { public: ... virtual ~Entity(); }; class Player: public Entity { ... }; using entity_collection = std::vector<std::shared_ptr<Entity>>; using reference_to_entity = std::weak_ptr<Entity>; }; 
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the clear explanation! This helped me a lot.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.