Writing a function#
int square(int x) {
return x * x;
}
void greet(const string &name) {
cout << "Hello, " << name << "\n";
}
A function names its return type (void for none), its parameters and their types. Define it above where it is used, or declare it there first with a prototype: int square(int x);.
Three ways to pass an argument#
This is the most important thing to get right in C++ functions.
By value: a copy. Changes stay inside the function.
void addOne(int x) { x++; } // the caller's variable is untouched
By reference: the original. Add &, and changes reach the caller.
void addOne(int &x) { x++; }
int n = 5;
addOne(n); // n is now 6
By const reference: the original, read-only. No copy, and no changes allowed.
long long sum(const vector<int> &nums) {
long long total = 0;
for (int x : nums) total += x;
return total;
}
The rule of thumb:
| Parameter is | Write |
|---|---|
a number, char or bool | by value: int x |
| something to read, like a vector or string | by const reference: const vector<int> &v |
| something the function must change | by reference: vector<int> &v |
Passing a vector by value copies every element. A helper called many times with a large vector by value can turn a fast solution into a slow one.
Default arguments#
int power(int base, int exponent = 2) {
int result = 1;
for (int i = 0; i < exponent; i++) result *= base;
return result;
}
power(5); // 25
power(2, 10); // 1024
Overloading#
Two functions may share a name if their parameters differ:
int largest(int a, int b) { return a > b ? a : b; }
double largest(double a, double b) { return a > b ? a : b; }
The compiler picks the one that matches the arguments. The standard max and min already do this for you.
Returning two values#
A pair holds two values, and structured bindings unpack it:
pair<int, int> minMax(const vector<int> &v) {
return {*min_element(v.begin(), v.end()), *max_element(v.begin(), v.end())};
}
auto [lo, hi] = minMax(nums);
For three or more, use tuple, or better, a small struct with named fields.
Recursion#
long long factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1);
}
Every recursive function needs a case it answers directly, or it calls itself until the program crashes.
Key points#
- Pass small values by value, things you only read by
const &, things you change by&. - Passing a vector by value copies all of it.
- Defaults make parameters optional; overloading reuses a name for different types.
pairandauto [a, b]return and unpack two values.
Next: vectors, C++'s growable arrays.