Reference member variables as members of a class

In my place of work, I see that this style is used widely: -

#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 suppose this will prevent, perhaps, the big overhead of copying a large complex object?

Is this generally good practice? Are there any pitfalls for this approach?

+43
c ++ reference
Sep 12 '12 at 11:34
source share
5 answers

Is there a name to describe this idiom?

In UML, this is called aggregation. It differs from composition in that the member object does not belong to the reference class. In C ++, you can implement aggregation in two different ways: using links or pointers.

I suppose this will prevent, perhaps, the big overhead of copying a large complex object?

No, that would be a very bad reason to use this. The main reason for aggregation is that the contained object does not belong to the contained object, and therefore their lifetime is not related. In particular, the resource referenced by the object must survive the corresponding one. Perhaps it was created much earlier and can live until the end of the life of the container. In addition, the state of the reference object is not controlled by the class, but may change externally. If the link 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 for this approach?

This is a design tool. In some cases this will be a good idea, in some it will not. The most common mistake is that the lifetime of the object containing the link should never exceed the lifetime of the reference object. If the surrounding object uses the link after destroying the reference object, you will have undefined behavior. In general, it is better to attribute the composition to aggregation, but if you need it, it will be a good tool, like any other.

+59
Sep 12 '12 at 12:30
source share

He called dependency injection through constructor injection : class A receives the dependency as an argument to its constructor and saves the reference to the dependent class as a private variable.

There is an interesting introduction to wikipedia .

For const-correctness, I would write:

 using T = int; class A { public: A(const T &thing) : m_thing(thing) {} // ... private: const T &m_thing; }; 

but the 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. 

Better to add (minimum C ++ 11 required):

 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 is pretty much guaranteed that you will not have a pointer to a temporary one .

In addition, since the constructor accepts a pointer, it is more clear to users of A so that they pay attention to the lifetime of the transmitted object. Strike>




A few related topics:

  • Should I prefer pointers or references in member data?
  • Using reference as class members for dependencies
  • Gotw # 88
  • Disable rvalue binding through constructor to reference to Member constant
+19
Sep 15 '14 at 10:23
source share

Is there a name to describe this idiom?

There is no name for this use, it is simply called Link as a Member of a Class.

I suppose this will prevent, perhaps, the big overhead of copying a large complex object?

Yes, as well as scenarios in which you want to associate the lifetime of one object with another object.

Is this generally good practice? Are there any pitfalls for this approach?

Depends on your use. Using any language feature is a "horse choice for courses." It is important to note that every (almost all) language function exists, because it is useful in some scenario.
There are several important points to consider when using references as members of a class:

  • You need to make sure that the specified object is guaranteed to exist until the class object exists.
  • You need to initialize the element in the initializer list of the constructor element. You cannot have lazy initialization , which might be possible with a pointer element.
  • The compiler will not generate a copy assignment operator=() , and you will have to provide it yourself. It is very difficult to determine what action the = operator should take in this case. Thus, basically your class becomes non-assignable .
  • Links cannot be NULL or made to refer to any other object. If you need to reinstall, then this is not possible with a link, as is the case with a pointer.

For most practical purposes (unless you are really interested in using large memory due to the size of the member), it is enough to have a member instance, not a pointer or reference element. This saves you from many problems related to other problems that the referenced / index elements encounter, but at the cost of additional memory usage.

If you must use a pointer, make sure you use a smart pointer instead of a raw pointer. That would make your life a lot easier with pointers.

+16
Sep 12 '12 at 11:55
source share

Member links are usually considered bad. They make life difficult compared to member pointers. But this is not particularly unrecognizable, and it is not a particular idiom or thing. This is just an overlay.

+1
Sep 12
source share

C ++ provides a good mechanism for controlling the lifetime of an object, although class / structure constructions. This is one of the best C ++ features over other languages.

When you have member variables opened via ref or a pointer, this basically breaks encapsulation. This idiom allows the class consumer to change the state of object A without it (A), which has any knowledge or control over it. It also allows the consumer to hold the pointer / pointer to the internal state, during the lifetime of object A. This is a poor design. Instead, the class can be reorganized to hold a ref / pointer to a shared object (not its own), and they can be set using the constructor (Mandate rules). The shared object class can be designed to support multithreading / concurrency, if applicable.

+1
Sep 12
source share



All Articles