Click here to Skip to main content
Click here to Skip to main content

C++ references - not as safe as houses, but they can be optimal

By , 18 Nov 2012
Rate this:
Please Sign up or sign in to vote.

Introduction 

C++ references are not as safe as houses. They aren't really a safety feature at all, they are more of an optimisation. C++ references are in some ways even more dangerous than pointers, they cannot be tested or reset and at run-time they will be assumed to be valid and may even have been optimised away.  This is offset by some compiler enforced grammar that prevents you from shooting yourself in the foot in some of the more obvious ways but it is only a token effort. At first sight C++ references may appear to offer a cushion of safety but it can easily be broken - and not just by obviously malicious or careless code. They are however, an excellent optimisation and, as long as you know you can trust them, a geat syntactical convenience. 

The following is an account of areas in which I have dared to venture. It could be seen as a tutorial but it is more one persons attempt to clarify his own use so that doubts do not undermine confidence in his own code. 

What is a C++ reference 

At first sight it might seem that a  C++ reference is just a syntactical sugar coating for a const pointer but there is more to it than that. 

A pointer, even a const pointer, has a value that can be read and tested (it could be null) and an address at which that value is stored. A  C++ reference does not. It is not an instruction to the compiler to create and hold an aliasing pointer, it is a direct instruction to make the alias. If the compiler can do some of this at compile time or optimise how it is done at run-time, it is free to do so. It is not obliged to hold an aliasing pointer. The C++ language does not give access to the aliasing pointer behind a  C++ reference or its address, because it may not exist.

To summarise: When you declare a pointer, you are telling the compiler to create space to store a pointer. When you declare a  C++ reference you are telling the compiler that that by this I mean that, fix it up for me! That is quite a big difference. With a C++ reference you are declaring your higher level intention and the compiler will optimise to that. 

It is appropriate that C++ references don't use the -> pointer dereference notation, there may well be no pointer, and it is entirely appropriate that operations are written as if they act on the object itself as that may well be what will happen at run-time -  certainly you have instructed the compiler to get as close as it can to this. 

In function parameter definitions 

For many years, the only use of C++ references that I trusted was in the definitions of function parameters that will take a value. 

void function(T& const t) 	//if I don't want t to be changed
//or
void function(T& t) 		//if I want to allow t to be changed 

I know that they will be passed a value declared variable sitting in the same scope as the call. So that variable is guaranteed to be valid throughout the execution of the function. It is not possible to call it cleanly in any other way. There is a syntactical convenience over passing pointers, and a performance gain over passing by value; t is not copied. Additionally the compiler is free to optimise away the reference as an entity in memory and its passage through the function call if it is able to. This is all win-win, Ï imagine that many programmers have now adopted this usage and are comfortable with it.

As a class member 

The only long living class member context in which I feel totally secure using  C++ references is as a back reference of a child to its parent:

class CChild
{
	CParent& m_Parent;
public:
	CChild::CChild(CParent& Parent)
		: m_Parent(Parent)
	{
	}
}; 

and this is only because I am confident that the child will be represented in the parent either by value or a single ownership smart pointer and this will ensure there can be no child without a parent. This is also a case where the compiler is likely to optimise m_Parent away completely as a run-time dereferencing intermediary. So although it may be the only use I can see for a C++ reference as a class member, it is a good one. 

Referencing elements of a collection 

Using C++ references in order to reference elements of a collection is potentially dangerous and remember, hitting an invalid C++ reference at run-time could be worse than hitting an invalid pointer. It can happen like this:

CAtlArray<T> Array;		//create an array
Array.Add(new T);		//add an element
T& t=v[0];			//take a reference to it
Array.RemoveAll();		//empty the array
t.do_something();		//use t and crash! 

OK - that one is easy to see but if T& t lives on through more code, it could easily be overlooked.

Despite this danger, I have recently ventured into this area because of the excellent optimisation it offered on a specific project. In this project I load up a set of pre-written tables. The key thing is that they are of fixed length and I do not add or remove any items, ever, though I may alter them. This is intrinsic to the design. Also intrinsic to the design is a need to load them from files very fast and to work intensely with their contents.

The first decision was to store the rows within the arrays that will hold the tables by value. The next was to pre-allocate the arrays to the known fixed size and to provide the elements with empty default constructors so that a block allocation of the array can take place without any construction code being executed. There is no point in initialising them because a block copy from the contents of a file will soon overwrite them all in one go. 

I knew all the time that I would want to take an element of an array and do quite a bit of work with it and I don't want to dereference it out of the array every time I want to refer to it and I definitely don't want a copy out to happen every time I do that. So I will want to take an alias to an element of an array and work with that. 

As a local variable  

Now I know that no array elements are going to be deleted or moved while I am working on them and although the work I am doing may be complex, it all takes place within the scope of a single function. Reassured by this I, for the first time, declared a C++ reference as a local variable, like this:

{
	CElement& Element=ArrayOfElements[i];
	Element.DoThis();
	Element DoThat();
} 

apart from being nice to look at it allows maximum optimisation by the compiler, more so than:

{
	CElement* pElement=&(ArrayOfElements[i]);
	pElement->DoThis();;
	pElement->DoThat();
} 

which obliges the compiler to provide a specific location for storing pElement whose address and value must be readable.

Either way there is the satisfaction of knowing that you are working on the array element itself rather than a copy of it .... but watch out with the syntax of the C++ reference. You only have to omit the ampersand and you have written:

{
	CElement Element=ArrayOfElements[i];
	Element.DoThis();
	Element DoThat();
} 

which is completely different - you are making a copy and are working with that. This is much less efficient and changes will be lost..

Anxious to avoid this so easily made mistake, I took the measure of providing my CElement classes with a dummy private copy constructor: 

class CElement 
{
private:
	CElement(CElement & Element )
	{
	}
public:
	CElement( )
	{
	}
}; 

This prevents public copies from being made so that:

CElement Element=ArrayOfElements[i]; 	//Error, copy constructor declared private 

will not compile.

As a return type 

In some cases I wanted to retrieve a reference to an element through special access functions, and had my first experience of declaring a C++ reference as a return type;

CElement& GetElement(int i)
{
	CElement& Element=ArrayOfElements[i];
	return Element;		//returning a C++ reference
}   

of course many collections do provide such access functions that return a reference but we are more familiar with using them to make a copy

CElement Element=GetElement(i); 	//Element is your copy to keep as long as you like   

They also offer the choice of working directly through the reference 

{
	CElement& Element=GetElement(i);	//Element is a reference to the element in the array
	Element.DoThis();
	Element DoThat();
	// but don't do anything here that will disturb the layout of the collection
}   

but it is advisable to scope the local reference declaration CElement& Element to a short lifetime and it is essential to ensure that nothing moves in that collection while you are using it. 

End note 

These have been my modest and cautious ventures into using C++ references.  My overall impression is that C++ references provide more direct programming and potentially more direct execution but from a safety point of view C++ references will not look after you, it is you that must look after them.  I am curious to hear the experience of others with them and how they view them.  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

john morrison leon
Software Developer (Senior)
Spain Spain
I love writing algorithms and creating infrastructures that solve problems and work with a lot of graphical visualisation if only to make sure that I can see it is all working properly. I do all of the serious work in C++.

Comments and Discussions

 
QuestionReferences Pinmembergeoyar24-Nov-12 15:14 
AnswerRe: References Pinmemberjohn morrison leon24-Nov-12 23:59 
GeneralRe: References Pinmembergeoyar25-Nov-12 15:22 
GeneralMy vote of 3 PinmentorPaul Watt20-Nov-12 13:31 
I like the topic.
I think you make some very good points regarding potential dangers in reference usage. Such as the case you describe when the original object pointed to by the reference is deleted.
 
However, your article reads more like an editorial with a biased opinion rather than an instructive tutorial intended to educate.
 
(Edited to finish the message in an edit window that can show more than 100 characters...)
 
You can test the address of a reference if you want to with the & to receive the address. This does not make sense because the address cannot be 0. This will not tell you if the reference is anymore valid than a dangling pointer.
 
There are basically two differences between references and pointers, which you elude to, but do not specifically call out. I think this is important because you have tagged the article with beginner:
 
1) References must be initialized at declaration, and may not be initialized to null.
2) References cannot be reassigned once declared
 
As you have said, references can be optimized away. Why not first consider how the code and usage will appear to the implementor of the code, and developers that use the code?
 
For instance, using call-by-reference for parameter type guarantees to the implementor that a value must be supplied for that parameter. This is useful when designing interfaces. Literal values can be provided if const& are used, but not for references alone as input parameters.
 
There is much more to references than you have pointed out.
 
I will continue to watch this article and consider raising my vote if you modify the article to be written from a neutral point-of-view and cleanly show when a reference should be chosen vs. a pointer and even a copied value on the stack.
GeneralRe: My vote of 3 Pinmemberjohn morrison leon20-Nov-12 14:41 
GeneralRe: My vote of 3 PinmentorPaul Watt20-Nov-12 15:48 
GeneralRe: My vote of 3 [modified] Pinmemberjohn morrison leon20-Nov-12 22:06 
GeneralRe: My vote of 3 PinmentorPaul Watt21-Nov-12 4:57 
GeneralRe: My vote of 3 Pinmemberjohn morrison leon21-Nov-12 10:44 
GeneralRe: My vote of 3 (Good Article, has the Potential to be a Great Article) PinmentorPaul Watt21-Nov-12 12:50 
QuestionReference seen as an alias versus reference seen as a syntactically disguised const pointer Pinmemberjohn morrison leon20-Nov-12 12:42 
GeneralMy vote of 1 PinmemberJoe Woodbury19-Nov-12 14:50 
GeneralRe: My vote of 1 Pinmemberjohn morrison leon20-Nov-12 1:59 
GeneralRe: My vote of 1 PinmemberJoe Woodbury20-Nov-12 3:48 
GeneralRe: My vote of 1 Pinmemberjohn morrison leon20-Nov-12 6:24 
GeneralRe: My vote of 1 PinmemberJoe Woodbury20-Nov-12 7:25 
GeneralRe: My vote of 1 Pinmemberjohn morrison leon20-Nov-12 10:15 
QuestionAs a class member... PinmemberRafa_19-Nov-12 6:35 
AnswerRe: As a class member... Pinmemberjohn morrison leon19-Nov-12 10:33 
AnswerRe: As a class member... PinmemberJoe Woodbury19-Nov-12 11:23 
GeneralRe: As a class member... PinmemberRafa_19-Nov-12 19:03 
GeneralRe: As a class member... Pinmemberjohn morrison leon20-Nov-12 2:12 
GeneralRe: As a class member... PinmemberRafa_20-Nov-12 5:12 
GeneralRe: As a class member... PinmemberJoe Woodbury20-Nov-12 3:44 
GeneralRe: As a class member... PinmemberRafa_20-Nov-12 5:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 18 Nov 2012
Article Copyright 2012 by john morrison leon
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid