×
Get in touch

Get in touch with Binary

Binary Studio website uses cookies to enhance your browsing experience. By continuing to use our site, you agree to our Privacy Policy and use of cookies.

Learn more more arrow
Agree
andrew.kramarenko@binary-studio.com'
Andrew Kramarenko C++ Developer 07.04.2014

Extended talk about references and pointers


.CPP files under the hood with pitfalls!

2. Extended talk about references and pointers

Introduction

This topic is continuing my previous article. I am going to make additional speech about reference and a little bit about pointers, because I have left behind some issues. Also my previous article wasn't small, however it didn't fit all information.

Now I will talk about some missed explanation examples, reference as return value or argument for function\method, overriding of operator & and covariation in C++.

Andrew from Binary Studio loves CPP.JPG

Extra explanation of pointers vs. references examples

or this song is nice, let's play it again

If you remember, in the previous article I said that reference point to a single object and pointer can point to  whole array of objects (in common sense, including primitive types). But I didn't say that reference can't point to another reference contrary to pointers.

This capability of pointers is very useful. For example I think everybody knows that matrix in mathematics and pointers suit well on low level. See:

So in code above we created matrix with dimensions 3x3, which is represented as pointer to array of pointers to integer arrays. To access each item of the matrix we can use operator []. See how we can print out the matrix:

And to prevent leaks you have to release such matrix correctly:

I use delete[] operator every time because we used operator new[] for allocation. Remember the rule: what was allocated with new[] has to be release with delete[].

As for references you can't make a reference to reference:

Actually there is C++11 syntax for r-value reference. I won't describe this now because this is a subject for another article, but if you want, you can look it up on the Internet. Just remember your compiler won't compile it for the purpose you want, but actually I don't know such purposes. Even more - don't think about it. It is a taboo, just like trying to create an array of references:

One more difference between pointers and references is a result of applying “address of” operator (“&”). Reference has the same address as the object it points to, but pointer doesn't:

Output in console will be different every time, but for example for me it printed out:

Address of value and reference is the same, which doesn't hold for pointers as I said earlier. In the last two cases you see the same address as value address because via &*ptr and ptr we get direct access to address of an object.

Keep value alive

or dead man doesn't walk

 

As you probably know local variables are allocated on stack. What will happen if one returns reference to such variable from a function\method? It is a horrible story, because call side of the function\method gets reference to dead object because all variables on a stack are released when their scope ends. So the following code causes segmentation fault (or not, the behavior is undefined):

We still can read some values from std::vector<int>&, because reference has the object address and nobody cares whether it is alive or not. Compiler doesn't care too and puts call of std::vector<T>::operator [] which therefore performs as usually and reads something from the released memory. On std::vector<T>::push_back() we might get segmentation fault, because called function tries to work with internal resources which don't exist anymore. So there is a hidden issue which can't be found immediately if you don't know about such cases or don't worry about compile warnings. The last one is very bad, dude.

If you want to return something by reference, commonly it may take place with fields of class and with usage of const modifier, which is still not considered to be a good practice. On the other hand, one can safely return reference to statically allocated object, which is widely used to control time and order of creation of such objects.

This is what I wanted to say about reference as return value, lets proceed with reference as input arguments.

Imagine that you have such function:

If you use it like this:

everything is good until you pass value 10 directly to function, in which case you'll get compile error:

Here 10 is r-value and as temporary object can be passed only by value, in our case compiler doesn't find function with such interface. If you change type argument from int& to int it will work, but there is a way to bind this r-value object. If you use const modifier for int& type it will compile and work:

This way it keeps 10 temporary alive and you can work with such object through constant operations. In our case it is readable. So if you don't change referenced argument it is a good practice to pass it with const modifier.

You can use the similar trick for keeping r-value temporary alive by binding it to a local variable which is constant reference. See code:

Here constant reference extends lifetime of temporary object until it goes from scope. So keep in mind that constant reference is your good friend.

 

Overriding operators and reference

or a little hack in the name of goodness

 

Everybody knows that you can override operators in C++. Operator &, obtaining address of class instance, can be overridden too. It can be necessary when some instance is wrapped by proxy class, but for some purpose instance of proxy class must return address of wrapped instance. The code looks like following:

For me it printed something like this:

As you can see it has printed the same addresses as for instance of class and as for its field, which I used like wrapped instance. But what can you do if you want to know real address of proxy class?

Of course you can return it by special method:

It printed for me:

This is the real address of proxy class, but it isn't cool to write such a method every time for every class. So there is std::addressof() helper function from <memory> header in blessed C++11. See:

But it can't be suitable for all folks, because what to do if your compiler doesn't support C++11 or has only partial support? And how to do it cross-platform? reinterpret_cast<T> hurries to help! Check out such hack:

See? reinterpret_cast<T> helps us to cast some reference to constant reference of unsigned char and cast it back to pointer type. In such a way pointer points to real address of instance and template function provides it for every type.

 

Return type covariation

or partly covariance

 

First of all lets define covariance. It is support of subtyping for inherited classes. For example if we have such inherited classes:

Covariance for such classes must guarantee that std::vector<CProgrammer> is subtype of std::vector<Human>. Here covariance means that std::vector<T> saves inheritance. So just a second ago you have read what C++ doesn't support. Templates don't provide it. They are invariant, because it will destroy type control.

However good news - C++ supports covariant return types via pointers and references. It means that you can change return type when overriding virtual methods. In such case types are covariant. Here is an example of overriding with different return type through pointers:

If you run such code:

You will get in output:

Same is true for references, but you can't do it with returning by value. It causes compile errors.

Such feature is used for implementing virtual constructor idiom and can be useful in other cases.

Conclusion

In this article I have written about some differences between references and pointers that were left out in my previous article. Also I have shown examples of keeping r-value alive by constant reference, underlined issue with returning reference to variable which is allocated on stack. As well I have described how to get address of class instance which has overloaded operator &. Finally we reviewed covariation of return type in C++. As usual you can find examples of code in my repository on Github.