I have a pretty clear understanding of how pointers, smart pointers and references work, but I'm having a really hard time wrapping my head around when I should use each of them. I've googled it a bit, but I still feel that I need some more clarity.
I very recently started using smart pointers and had been using raw pointers for most things before. Now I understand that I should avoid raw pointers whenever I'm not forced to it by the API I'm using (I'm programming against SDL, which uses raw pointers for its own types). So now I'm trying to figure out when to use references, when to use unique_ptr, when to use shared_ptr and when to use weak_ptr. I'm also not very sure about when to create new objects on the heap and when to create them on the stack/static space, although I feel that I'm starting to understand that better now. I come from Java, where I didn't really have to think about any of this.
I guess I'll try to give some concrete examples of situations in which I am unsure.
Passing to a function that won't store the object
Let's say that I have have a member function of a collider class like this:
double BoxCollider::getXCollision(Collider & collider)
{
//First check which type of Collider the argument is (BoxCollider or CircleCollider). Should I use dynamic_cast for this? Seems a bit tricky on references.
//Check if and how much the two colliders
}
Should I be using a reference here? As it is right now, each game object has a unique_ptr to its own collider, which is allocated on the heap before the game object is created. Is it a good idea to use references to objects that are heap allocated and stored as a unique_ptr? My question here is: should I ALWAYS use references as arguments to functions that will process the argument but not store it, if I expect it to not be null (although I can never guarantee it to not be null)? Or are there times when I should use some kind of pointer for this? Passing unique_ptr's are obviously not possible. Also, in this particular case, I need to check if the collider is a CircleCollider or a BoxCollider, which seems more tricky on references than on pointers. Should I still use a reference and use a nestled try/catch instead of a if/else to check the type?
Passing to a function or constructor that will store the object
Let's take a look at my GameObject constructor:
GameObject::GameObject(double xPosition, double yPosition, std::unique_ptr<GraphicsComponent> graphics, std::unique_ptr<PhysicsComponent> physics, std::unique_ptr<InputComponent> input) :
graphics{ std::move(graphics) }, physics{ std::move(physics) }, input{ std::move(input) }, xPosition{ xPosition }, yPosition{ yPosition }
{
}
As you can see, I'm using the component pattern for GraphicsComponent, PhysicsComponent and InputComponent. Right now I'm using unique_ptr to refer to them as they are all constructed on the heap before they are sent to GameObject's constructor. Does this make sense?
I have another example: GameObject has a function called setWorld that tells the GameObject which world it is in. Currently that function takes in a weak_ptr because I don't want the GameObject to own the world as that could cause problems.
void GameObject::setWorld(std::weak_ptr<World> world)
{
this->world = world;
}
I realized though that the only thing that should own the World object is my main Game object, so would it make more sense to use a unique_ptr to store it in the Game class and a reference to store it in the GameObject class? Or should I even store World as a plain object in Game as the world is loaded in Game? Like this:
class Game
{
public:
Game()
~Game()
//Other stuff
private:
World world;
}
How do I know if I want to store something as a pointer, as a reference or as a plain object?
I was sure that there were something else regarding references that I was uncertain about, but maybe my brain sorted it out. I'll write it here if I come up with more questions. Would be thankful for any help and clarification!