Click here to Skip to main content
15,867,568 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

 
QuestionReferences Pin
geoyar24-Nov-12 15:14
professionalgeoyar24-Nov-12 15:14 
AnswerRe: References Pin
john morrison leon24-Nov-12 23:59
john morrison leon24-Nov-12 23:59 
GeneralRe: References Pin
geoyar25-Nov-12 15:22
professionalgeoyar25-Nov-12 15:22 
GeneralMy vote of 3 Pin
Paul M Watt20-Nov-12 13:31
mentorPaul M Watt20-Nov-12 13:31 
GeneralRe: My vote of 3 Pin
john morrison leon20-Nov-12 14:41
john morrison leon20-Nov-12 14:41 
GeneralRe: My vote of 3 Pin
Paul M Watt20-Nov-12 15:48
mentorPaul M Watt20-Nov-12 15:48 
GeneralRe: My vote of 3 Pin
john morrison leon20-Nov-12 22:06
john morrison leon20-Nov-12 22:06 
GeneralRe: My vote of 3 Pin
Paul M Watt21-Nov-12 4:57
mentorPaul M Watt21-Nov-12 4:57 
GeneralRe: My vote of 3 Pin
john morrison leon21-Nov-12 10:44
john morrison leon21-Nov-12 10:44 
GeneralRe: My vote of 3 (Good Article, has the Potential to be a Great Article) Pin
Paul M Watt21-Nov-12 12:50
mentorPaul M Watt21-Nov-12 12:50 
QuestionReference seen as an alias versus reference seen as a syntactically disguised const pointer Pin
john morrison leon20-Nov-12 12:42
john morrison leon20-Nov-12 12:42 
GeneralMy vote of 1 Pin
Joe Woodbury19-Nov-12 14:50
professionalJoe Woodbury19-Nov-12 14:50 
GeneralRe: My vote of 1 Pin
john morrison leon20-Nov-12 1:59
john morrison leon20-Nov-12 1:59 
GeneralRe: My vote of 1 Pin
Joe Woodbury20-Nov-12 3:48
professionalJoe Woodbury20-Nov-12 3:48 
GeneralRe: My vote of 1 Pin
john morrison leon20-Nov-12 6:24
john morrison leon20-Nov-12 6:24 
GeneralRe: My vote of 1 Pin
Joe Woodbury20-Nov-12 7:25
professionalJoe Woodbury20-Nov-12 7:25 
Again, I used the word object in the general, compiler sense, not the OOP sense. If you prefer, a reference is an entity; it is not a preprocessor define (though a preprocessor could conceivably deal with them (the C++ standard does not specify how references are to implemented.)

john morrison leon wrote:
in the trivial case of using a C++ reference as an alias within the same scope, there is no reason for any object to exist,


But you don't know that. Just because the compiler could optimize it out of existence, doesn't mean it will. Furthermore, why would anyone use a reference in a trivial case? To extrapolate what happens in a trivial case to rules governing how a reference works in general is misleading and unhelpful. References are typically used to pass items to functions/methods or having items returned from the same.

While the standard only defines the behavior of references, even stating "[i]t is unspecified whether or not a reference requires storage", implementing them must be done in a concrete way and outside the trivial case, that way would be through pointers for the simple reason that the address of the item being referenced is usually not specified until run-time.

john morrison leon wrote:
But do not think that the behaviour of compilers defines the language, that is done elsewhere and compiler writers try to follow it.


Compilers don't define a language, but they define the implementation of a language. If you aren't going to respect that, why program in C++ at all? The primary point of C++ is that you are trying to implement code using a relatively high level of performance and to do that you must know the relationship between your code and what the compiler is doing. In all seriousness, if a developer isn't concerned about what that relationship is, they should be using a "higher level" language where that is of little concern, such .NET, Java, Python, etc. (Having said that,
knowing how JIT works can be very useful, but it isn't of primary concern.)

john morrison leon wrote:
I am trying to provide one and you seem to object to the idea of C++ references having that status.


My problem is that you didn't state that clearly. You can't make a series of concrete assertions and then claim that you were speaking metaphorically. If your intent was to say that references CAN be thought of as symbolic links, then say that.

As for specific criticisms; I gave several and all you've done is argue in the face of concrete facts refuting your arguments. I suggest you rewrite your article with the following as the basis of your opening paragraph:

john morrison leon wrote:
...what compilers do and there is what the C++ standards say and between those two programmers need to form coherent conceptual models in their minds that they can work with. For many programmers C++ references is an area where the conceptual model tends to lack both clarity and consensus.

GeneralRe: My vote of 1 Pin
john morrison leon20-Nov-12 10:15
john morrison leon20-Nov-12 10:15 
QuestionAs a class member... Pin
Rafa_19-Nov-12 6:35
Rafa_19-Nov-12 6:35 
AnswerRe: As a class member... Pin
john morrison leon19-Nov-12 10:33
john morrison leon19-Nov-12 10:33 
AnswerRe: As a class member... Pin
Joe Woodbury19-Nov-12 11:23
professionalJoe Woodbury19-Nov-12 11:23 
GeneralRe: As a class member... Pin
Rafa_19-Nov-12 19:03
Rafa_19-Nov-12 19:03 
GeneralRe: As a class member... Pin
john morrison leon20-Nov-12 2:12
john morrison leon20-Nov-12 2:12 
GeneralRe: As a class member... Pin
Rafa_20-Nov-12 5:12
Rafa_20-Nov-12 5:12 
GeneralRe: As a class member... Pin
Joe Woodbury20-Nov-12 3:44
professionalJoe Woodbury20-Nov-12 3:44 
GeneralRe: As a class member... Pin
Rafa_20-Nov-12 5:04
Rafa_20-Nov-12 5:04 

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.