Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C++

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

Rate me:
Please Sign up or sign in to vote.
3.50/5 (8 votes)
18 Nov 2012CPOL7 min read 37.9K   13   52
A brief excursion into practical contexts in which C++ references can be used effectively with discussion of the dangers in using them.

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. 

C++
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:

C++
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:

C++
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:

C++
{
	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:

C++
{
	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:

C++
{
	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: 

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

This prevents public copies from being made so that:

C++
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;

C++
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

C++
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 

C++
{
	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)


Written By
Retired
Spain Spain
Software Author with engineering, science and mathematical background.

Many years using C++ to develop responsive visualisations of fine grained dynamic information largely in the fields of public transport and supply logistics. Currently interested in what can be done to make the use of C++ cleaner, safer, and more comfortable.

Comments and Discussions

 
GeneralRe: As a class member... Pin
Rafa_20-Nov-12 5:15
Rafa_20-Nov-12 5:15 
QuestionOptimization? Pin
JackDingler19-Nov-12 5:50
JackDingler19-Nov-12 5:50 
AnswerRe: Optimization? Pin
Rafa_19-Nov-12 6:21
Rafa_19-Nov-12 6:21 
GeneralRe: Optimization? Pin
JackDingler19-Nov-12 6:32
JackDingler19-Nov-12 6:32 
GeneralRe: Optimization? Pin
Rafa_19-Nov-12 7:02
Rafa_19-Nov-12 7:02 
GeneralRe: Optimization? Pin
JackDingler19-Nov-12 7:05
JackDingler19-Nov-12 7:05 
AnswerRe: Optimization? Pin
john morrison leon19-Nov-12 8:31
john morrison leon19-Nov-12 8:31 
QuestionPuzzled by assertion Pin
Joe Woodbury19-Nov-12 4:30
professionalJoe Woodbury19-Nov-12 4:30 
AnswerRe: Puzzled by assertion Pin
john morrison leon19-Nov-12 8:59
john morrison leon19-Nov-12 8:59 
GeneralRe: Puzzled by assertion Pin
Joe Woodbury19-Nov-12 9:15
professionalJoe Woodbury19-Nov-12 9:15 
GeneralRe: Puzzled by assertion Pin
john morrison leon19-Nov-12 9:40
john morrison leon19-Nov-12 9:40 
GeneralRe: Puzzled by assertion Pin
Joe Woodbury19-Nov-12 11:18
professionalJoe Woodbury19-Nov-12 11:18 
GeneralRe: Puzzled by assertion Pin
john morrison leon19-Nov-12 12:06
john morrison leon19-Nov-12 12:06 
GeneralRe: Puzzled by assertion Pin
Joe Woodbury19-Nov-12 14:49
professionalJoe Woodbury19-Nov-12 14:49 
GeneralRe: Puzzled by assertion Pin
john morrison leon20-Nov-12 2:01
john morrison leon20-Nov-12 2:01 
Questionreferences to reference elements?? Pin
Espen Harlinn18-Nov-12 10:27
professionalEspen Harlinn18-Nov-12 10:27 
AnswerRe: references to reference elements?? Pin
john morrison leon18-Nov-12 11:25
john morrison leon18-Nov-12 11:25 
GeneralRe: references to reference elements?? Pin
Espen Harlinn18-Nov-12 11:35
professionalEspen Harlinn18-Nov-12 11:35 
GeneralRe: references to reference elements?? Pin
john morrison leon18-Nov-12 12:01
john morrison leon18-Nov-12 12:01 
GeneralRe: references to reference elements?? Pin
Espen Harlinn18-Nov-12 12:25
professionalEspen Harlinn18-Nov-12 12:25 
GeneralRe: references to reference elements?? Pin
john morrison leon19-Nov-12 9:28
john morrison leon19-Nov-12 9:28 
GeneralRe: references to reference elements?? Pin
Espen Harlinn19-Nov-12 9:56
professionalEspen Harlinn19-Nov-12 9:56 
GeneralRe: references to reference elements?? Pin
john morrison leon21-Nov-12 1:44
john morrison leon21-Nov-12 1:44 
GeneralRe: references to reference elements?? Pin
Espen Harlinn21-Nov-12 3:00
professionalEspen Harlinn21-Nov-12 3:00 
GeneralRe: references to reference elements?? Pin
john morrison leon22-Nov-12 1:52
john morrison leon22-Nov-12 1:52 

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

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