When designing an interface for passing objects which are meant to be stored for later use and which should not be 'null', I am always a bit uncertain if the argument should be passed by reference or as a pointer.
This is an example of what I mean:
class Foo
{
private:
Bar* m_bar;
public:
Foo() : m_bar(nullptr)
{
}
void Register(Bar& bar)
{
m_bar = &bar;
m_bar->Registered();
}
// -- OR --
void Register(Bar* const bar)
{
if (bar == nullptr)
{
// Error!
}
m_bar = bar;
m_bar->Registered();
}
// Some method makes use of the stored pointer later
void DoSomething()
{
if (m_bar == nullptr)
{
// Error!
}
m_bar->DoOtherThing();
}
};
My thoughts on this are:
- The passed in reference may go out of scope before
DoSomething
gets called, but that may happen with the pointed to object as well. - Using the pass-by-non-const-reference version gets rid of duplicating the check for null and tells the caller that it is not possible to register 'nothing'.
- It would be better to pass a reference in the constructor of
Foo
as it is required byDoSomething
, but sometimes it this is not an option. - It would be better to pass a reference to
DoSomething
directly, but again, this is not always possible.
So if I need that kind of separate setter/register method, would it be clearer to use a reference or a pointer?
PS I know there is are two very similar questions Storing a pass-by-reference parameter as a pointer – Bad practice? and Reference vs dereference pointers in arguments C++/C, but I think they both are concerned with slightly different problems. The former deals mostly with the (ab)use of const
and the latter does not say anything about storing a pointer. There may be another question/answer out there, if so, please just mark this a duplicate then!
Best Answer
Preparing my flame-retardant suit, as I feel some bias must be present in any answer to this question.
First, I would like to note that I work within the embedded world, so RAII and stack allocation are generally the way to go. In almost any circumstance, I see passing something in by reference as a good idea because of the obvious reason: it can't be NULL. But notably, this is because I primarily do stack allocation - so I'm not typically taking something from the heap and stuffing it into a reference when passing it in. This style of programming eliminates common sources of issues for concerns 1, 3, and 4 that you listed.
If I do pass in a pointer, it's for one of two reasons: 1) NULL really is an option (somewhat rare) or 2) I want to signal that my object is taking ownership of the one being passed in. I realize this comes down somewhat to style and domain, but it's a rule of thumb that's helped me make fewer errors and communicate with the programmers on my team about what my code is doing.