C++ pass by reference

Posted on April 4, 2018 by Aidan Delaney

I introduced two changes into my current Intro to C++ course for first year undergraduates. The first is the open access text book. The second is in introducing functions before I even introduce conditionals and iteration. By introducing functions first I think I’ve introduced abstraction and generalisation and changed how students see code. We’re now half way through the course and I added a small chapter to the open access book on passing parameters by reference.

This is pretty cool. The book now corresponds exactly to how I want to teach the course (and not the other way around). It’s also great because I never have to introduce raw pointers in CS1. I’ll introduce smart pointers, but only students who go on to specialise in computing or engineering need to grasp the C++ memory model in second year. My first year students, CS, engineering, maths, accounting and education people, can concentrate on computational thinking over and above the semantics of pointee life-cycles.

Passing int

Consider the following function:

We may call it from main (or any other function) as follows:

In the above code we make 2 copies of the int. We originally declare and instantiate my_val. In the call to increment we create a copy of the my_val and pass it to the function. In the function we modify the copy and then create a new copy to return to main. This is because we are passing the int and returning the int by copy.

In the case of an int we’re generally happy to pass it by copy. An int is generally 4 bytes (32 bits) in size. It’s cheap and easy to copy an int. However, consider the following:

In the above code snippet we deliberately create a large chunk of memory. After the for loop, the variable strings occupies about 8Mb of memory. When passing this to a function we don’t want to have to create another 8Mb copy of the variable. It would be much nicer if we could pass some kind of reference to strings.

Pass by reference

Passing by reference is denoted using the & symbol. So when you see int & you read it as an int reference. So, the following method takes parameters by copy:

whereas the next snippet passes the parameter by reference:

You can mix and match how you pass parameters in a function call. The following snippet passes one parameter by reference but another by copy:

We pass parameters by reference if we wish to modify them. We pass large things by const reference when we don’t wish to modify them:

In the above snippet the const precludes any modifications of vs. That is to say, we can’t call vs.push_back("") in the example method as that would modify vs. We can read the size of vs doing vs.size() as that operation does not violate const-ness.

Return by reference

As a rule, don’t do this. You can break this rule of thumb if you are return a reference that has been passed to the function.

One reason that we don’t do this is to avoid returning a reference to a deleted local variable. Consider the following function which returns an int reference:

If I compile it on a modern C++ compiler I get a warning. However, this warning doesn’t stop the code from compiling.

If we used the reference returned from square in main then the behaviour is undefined. In the best case your application will crash.

What we can now do

We can no pass large structures by reference or by const reference.