Reference member variables as class members
In my place of work I see this style used extensively:-
#include <iostream>
using namespace std;
class A
{
public:
A(int& thing) : m_thing(thing) {}
void printit() { cout << m_thing << endl; }
protected:
const int& m_thing; //usually would be more complex object
};
int main(int argc, char* argv[])
{
int myint = 5;
A myA(myint);
myA.printit();
return 0;
}
Is there a name to describe this idiom? I am assuming it is to prevent the possibly large overhead of copying a big complex object?
Is this generally good practice? Are there any pitfalls to this approach?
Solution 1:
Is there a name to describe this idiom?
In UML it is called aggregation. It differs from composition in that the member object is not owned by the referring class. In C++ you can implement aggregation in two different ways, through references or pointers.
I am assuming it is to prevent the possibly large overhead of copying a big complex object?
No, that would be a really bad reason to use this. The main reason for aggregation is that the contained object is not owned by the containing object and thus their lifetimes are not bound. In particular the referenced object lifetime must outlive the referring one. It might have been created much earlier and might live beyond the end of the lifetime of the container. Besides that, the state of the referenced object is not controlled by the class, but can change externally. If the reference is not const
, then the class can change the state of an object that lives outside of it.
Is this generally good practice? Are there any pitfalls to this approach?
It is a design tool. In some cases it will be a good idea, in some it won't. The most common pitfall is that the lifetime of the object holding the reference must never exceed the lifetime of the referenced object. If the enclosing object uses the reference after the referenced object was destroyed, you will have undefined behavior. In general it is better to prefer composition to aggregation, but if you need it, it is as good a tool as any other.
Solution 2:
It's called dependency injection via constructor injection: class A
gets the dependency as an argument to its constructor and saves the reference to dependent class as a private variable.
There's an interesting introduction on wikipedia.
For const-correctness I'd write:
using T = int;
class A
{
public:
A(const T &thing) : m_thing(thing) {}
// ...
private:
const T &m_thing;
};
but a problem with this class is that it accepts references to temporary objects:
T t;
A a1{t}; // this is ok, but...
A a2{T()}; // ... this is BAD.
It's better to add (requires C++11 at least):
class A
{
public:
A(const T &thing) : m_thing(thing) {}
A(const T &&) = delete; // prevents rvalue binding
// ...
private:
const T &m_thing;
};
Anyway if you change the constructor:
class A
{
public:
A(const T *thing) : m_thing(*thing) { assert(thing); }
// ...
private:
const T &m_thing;
};
it's pretty much guaranteed that you won't have a pointer to a temporary.
Also, since the constructor takes a pointer, it's clearer to users of A
that they need to pay attention to the lifetime of the object they pass.
Somewhat related topics are:
- Should I prefer pointers or references in member data?
- Using reference as class members for dependencies
- GotW #88
- Forbid rvalue binding via constructor to member const reference