How a parameter is declared decides whether the function gets a copy, an alias to the caller's object, or its address.
The three ways
#include <iostream>
void byValue(int x) { x = 100; std::cout << "inside " << x << '\n'; } // a copy
void byReference(int& x) { x = 200; } // x IS the caller's variable
void byPointer(int* x) { if (x) *x = 300; } // modifies through the address (may be null)
int main() {
int a = 1;
byValue(a); std::cout << a << '\n';
byReference(a); std::cout << a << '\n';
byPointer(&a); std::cout << a << '\n';
byPointer(nullptr); // allowed: pointers can express “no object”
}inside 100
1
200
300| Style | Copies? | Can modify caller? | Can be null? | Use for |
|---|---|---|---|---|
T x | Yes | No | — | Cheap types (int, double, small structs, iterators), or when you need a copy anyway |
const T& x | No | No | No | Read-only access to anything non-trivial (std::string, std::vector) |
T& x | No | Yes | No | Out/in-out parameters |
T* x | No | Yes | Yes | Optional objects, C APIs |
T&& x | No | Yes (steals) | No | Taking ownership of temporaries (move semantics) |
Why const& for big objects
#include <iostream>
#include <string>
struct Tracker {
std::string name;
Tracker(std::string n) : name(std::move(n)) {}
Tracker(const Tracker& other) : name(other.name) { std::cout << "copy!\n"; }
};
void slow(Tracker t) { std::cout << "slow " << t.name << '\n'; }
void fast(const Tracker& t) { std::cout << "fast " << t.name << '\n'; }
int main() {
Tracker big("report");
slow(big); // copies the whole object
fast(big); // no copy
}copy!
slow report
fast reportSwapping — the classic example
#include <iostream>
void swapRef(int& a, int& b) { int t = a; a = b; b = t; }
void swapPtr(int* a, int* b) { int t = *a; *a = *b; *b = t; }
int main() {
int x = 1, y = 2;
swapRef(x, y);
std::cout << x << y << '\n';
swapPtr(&x, &y);
std::cout << x << y << '\n';
}21
12Passing arrays and containers
#include <iostream>
#include <vector>
int sumArray(const int* arr, std::size_t n) { // a C array decays to a pointer: size is lost
int s = 0;
for (std::size_t i = 0; i < n; ++i) s += arr[i];
return s;
}
void doubleAll(std::vector<int>& v) { for (int& x : v) x *= 2; }
int main() {
int raw[] = {1, 2, 3};
std::cout << sumArray(raw, 3) << '\n';
std::vector<int> v{1, 2, 3};
doubleAll(v);
std::cout << v[0] << v[1] << v[2] << '\n';
}6
246Is Java/Python-style pass by reference the same as C++ references?
When would you pass by pointer instead of reference?