Passing variables to functions in C++
When you call a function, you choose how the function receives the variable.
The most common options are:
| Method | Syntax | Can change caller variable? | Can be null? | Common use |
|---|---|---|---|---|
| Pass by value | void f(int x) |
No | No | Small values, copy is OK |
| Pass by reference | void f(int& x) |
Yes | No | Function must modify the variable |
| Pass by const reference | void f(const std::string& s) |
No | No | Read large object without copy |
| Pass by pointer | void f(int* p) |
Yes, if not null | Yes | Optional object, C API style |
| Pass by const pointer | void f(const int* p) |
No | Yes | Optional read-only object |
| Pass by rvalue reference | void f(std::string&& s) |
Function can steal/move | No | Move semantics |
Pass by value
The function gets a copy.
Changing the parameter does not change the original variable.
Output:
Use pass by value when:
- the variable is small, like
int,float,double,bool,char - the function needs its own copy
- you do not want to change the caller variable
Pass by reference
The function receives another name for the same variable.
Changing the parameter changes the original variable.
int& x means:
xis a reference to an existingint.
Use pass by reference when the function should modify the original variable.
Warning
A non-const reference makes it clear that the function may change the input.
Pass by const reference
Pass by const reference means:
- do not copy the object
- do not allow the function to modify it
This is very common for large objects like std::string, std::vector, and custom classes.
Use const T& when the function only reads a large object.
Pass by pointer
A pointer stores an address.
p points to value.
Inside a function, use *p to access the value.
Important syntax:
| Syntax | Meaning |
|---|---|
int* p |
p is a pointer to an int |
&number |
address of number |
*p |
value at the address |
nullptr |
pointer points to nothing |
Use pointers when:
- the argument is optional
- you need to pass
nullptr - you are working with C libraries
- the codebase already uses pointer style for this API
If the argument must exist, prefer a reference.
Const and pointers
Pointer const syntax is confusing because const can apply to the value, the pointer, or both.
Read the declaration from right to left.
Pointer to const value
const int* p means:
- the value is const
- the pointer can point somewhere else
Const pointer to mutable value
int* const p means:
- the value can change
- the pointer cannot point somewhere else
Const pointer to const value
const int* const p means:
- the value cannot change
- the pointer cannot point somewhere else
Passing arrays
A C-style array usually becomes a pointer when passed to a function.
In modern C++, prefer std::vector, std::array, or std::span.
std::span is available from C++20.
Return value instead of output parameter
Often the cleanest method is not to modify an argument.
Return the result instead.
For multiple return values, use a struct.
This is usually easier to read than output parameters.
Output parameters
An output parameter is an argument that the function writes into.
This style is useful when:
- the function can fail
- you do not want exceptions
- the codebase uses this pattern
For new code, also consider std::optional.
Pass by rvalue reference and move
This is more advanced, but you will see it in real C++.
T&& usually means the function can take ownership of a temporary object.
std::move does not move by itself. It allows moving from an object.
After moving, the object is still valid, but its value should not be trusted.
For beginner code, first learn:
- value
- reference
- const reference
- pointer
Then learn move semantics.
Function parameters in class methods
Class methods use the same rules.
void print() const means this method does not change the object.
This is different from const std::string& name.
| Const location | Meaning |
|---|---|
void print() const |
method does not modify this object |
const std::string& name |
function does not modify name |
const int* p |
function does not modify *p |
Class method with const reference
This avoids copying the argument when calling the function.
Class method with value and move
Another common modern style is pass by value, then move into the class member.
This works well when the class needs to keep its own copy anyway.
Quick rules
Use these rules most of the time:
| Situation | Recommended parameter |
|---|---|
| Small read-only value | int x |
| Need to modify caller variable | int& x |
| Large read-only object | const std::string& s |
| Optional object | Robot* robot |
| Optional read-only object | const Robot* robot |
| Function takes ownership | std::unique_ptr<T> |
| Function stores a copy | std::string name, then std::move(name) |
Common mistakes
Mistake: expecting value parameter to modify original
Fix:
Mistake: forgetting to check pointer
Fix:
Mistake: copying large objects for no reason
Fix:
Practice
Try to answer before checking:
Which functions can change the original int?
Answer:
acannotbcanccannotdcan, ifx != nullptrecannot