The answer depends on what you are trying to model:
- If an engine cannot exist outside the context of its car, and if a car cannot exist without an engine, returning a reference is fine - This means that the car is going to exist at the time when you work with its engine.
- If an engine can exist outside the context of its car, you would be better off returning a pointer (preferably, a smart pointer). This way deleting the corresponding car is not going to delete the engine.
- Same goes if a car can exist without an engine: you need to return a pointer, because there is no such thing as null reference (hacks do not count here)
Of course these are only models of the reality - in reality, both cars without engines and engines without cars do exist. You can even swap engines between cars if they fit. However, modeling the reality to this level takes more effort on your part, so you need to make a decision on how far your model goes.
The difference between engine en = c.get_engine();
and engine &en = c.get_engine();
is that the first expression makes a copy of c
's engine, while the second one references the engine in place. Among other things, this means that manipulations on the copy of the engine are not automatically reflected in the original, because they are not the same object. Another consequence is that a copy survives deletion of the car, while a reference does not.