Introduction
C++ gives functions three new tools. References let a function change its caller's variables without the pointer syntax of C. Overloading lets one name cover several related jobs, and default arguments let a call leave out the values that are usually the same.
How it works
References
#include <iostream>
using namespace std;
void swapByValue(int a, int b) {
int t = a;
a = b;
b = t;
}
void swapByRef(int& a, int& b) {
int t = a;
a = b;
b = t;
}
int main() {
int x = 1, y = 2;
swapByValue(x, y);
cout << x << " " << y << endl;
swapByRef(x, y);
cout << x << " " << y << endl;
int& alias = x;
alias = 10;
cout << "x is now " << x << endl;
return 0;
}int& a declares a reference: another name for an existing int. When swapByRef(x, y) runs, a is x and b is y, so swapping them swaps x and y. In the visualizer a reference is drawn as an arrow to the variable it names. swapByValue gets copies instead, so x and y do not change.
A reference does the job of a C pointer parameter (int *a and *a = ...) with less noise: no & at the call and no * inside. Two rules make it safe:
- a reference must be bound to something when it is created (
int& r;is an error); - it can never be re-bound:
alias = 10stores 10 inx, it does not makealiasrefer to 10.
const references (const string& s) let a function read a big object without copying it, and promise not to change it. They can also take temporary values, such as "text" or a + b.
Syntax
void swapRef(int& a, int& b); // a and b are the caller's variables
double total(const vector<double>& v); // read without copying
int area(int side); // overloads: same name,
int area(int w, int h); // different parameters
void greet(string name, string greeting = "Hello");
Example
#include <iostream>
#include <string>
using namespace std;
double area(double r) { return 3.14159 * r * r; } // a circle
int area(int w, int h) { return w * h; } // a rectangle
int area(int side) { return side * side; } // a square
void greet(string name, string greeting = "Hello") {
cout << greeting << ", " << name << "!" << endl;
}
int main() {
cout << area(2.0) << " " << area(3, 4) << " " << area(5) << endl;
greet("Rahim");
greet("Karim", "Assalamu alaikum");
return 0;
}C++ allows several functions with the same name if their parameter lists differ: this is overloading. The compiler picks the version whose parameters best match the arguments: area(2.0) takes a double, so the circle version runs; area(3, 4) has two ints.
A parameter with a default argument (string greeting = "Hello") can be left out at the call. Defaults must come at the end of the parameter list.
Line by line
- Three functions are all called
area. One takes adouble, one takes twoints, and one takes oneint. greethas a default argument, so its second parameter can be left out.area(2.0)has adoubleargument, so the circle version runs and gives about 12.5664.area(3, 4)has twoints, so the rectangle version runs and gives 12.area(5)has oneint, so the square version gives 25.greet("Rahim")leaves out the greeting, so"Hello"is used.greet("Karim", "Assalamu alaikum")gives both arguments, so the default is not used.
Expected result
12.5664 12 25
Hello, Rahim!
Assalamu alaikum, Karim!
In the real world
The standard library is full of overloads: to_string works for every number type, and abs and max have versions for each type. Large objects such as strings, vectors and images are passed by const& throughout real C++ code, because copying them would waste time and memory.
Common mistakes
- Passing a value to a non-const reference:
swapByRef(x, 5)is an error, because 5 is not a variable. - Overloads that differ only in their return type are not allowed: the call would not tell them apart.
- Returning a reference to a local variable: it refers to memory that is gone when the function ends.
Best practices
- Pass small built-in values such as
intby value, and large objects byconst&. - Use a non-const reference only when the function is meant to change the argument.
- Keep overloads doing the same kind of job, so a reader is never surprised.
Summary
A reference is another name for a variable, so a function can change its caller's values, or read them without copying when the reference is const. Overloading reuses a name for different parameter lists, and default arguments fill in what a call leaves out.
Quick check
Key takeaways
- A reference must be bound when it is made, and cannot be re-bound.
- const T& reads a big object without copying it.
- The compiler picks an overload from the arguments' types.
- Default arguments must come at the end of the parameter list.
Practice problems for this lesson
My notes
Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.