Functions
A function is a named, reusable block of code that performs a specific task. Functions are the primary tool for organising C++ programs — they eliminate code duplication, improve readability, and make programs easier to test. Every C++ program is a collection of functions coordinated by main.
Declaring and Defining Functions
#include <iostream>
using namespace std;
// Declaration (prototype) — tells the compiler the function exists
// Place these at the top or in a header file
double square(double x);
int add(int a, int b);
int main() {
cout << square(5.0) << "\n"; // 25
cout << add(3, 4) << "\n"; // 7
return 0;
}
// Definitions — can appear after main if declared above
double square(double x) {
return x * x;
}
int add(int a, int b) {
return a + b;
}Without the forward declaration, you would have to define functions before main — declarations give you flexibility in file organisation.
Return Types
#include <iostream>
using namespace std;
// Returns a value
int multiply(int a, int b) {
return a * b;
}
// Returns nothing
void printLine(const string& text) {
cout << text << "\n";
// No return statement needed (or use bare `return;`)
}
// Early return — exits as soon as condition is met
bool isEven(int n) {
if (n % 2 == 0) return true;
return false;
// Or simply: return (n % 2 == 0);
}
// Multiple return points
string classify(int score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
return "F";
}
int main() {
printLine("Hello from function");
cout << multiply(4, 7) << "\n"; // 28
cout << isEven(10) << "\n"; // 1
cout << classify(85) << "\n"; // B
return 0;
}Pass by Value vs Pass by Reference
#include <iostream>
using namespace std;
// PASS BY VALUE — function gets a COPY; original is unchanged
void doubleByValue(int x) {
x *= 2; // Only the local copy is modified
}
// PASS BY REFERENCE — function works on the ORIGINAL variable
void doubleByRef(int& x) {
x *= 2; // Modifies the caller's variable
}
// PASS BY CONST REFERENCE — read the original, no copying
double sumVector(const vector<int>& v) {
double total = 0;
for (int n : v) total += n;
return total;
}
int main() {
int a = 10;
doubleByValue(a);
cout << a << "\n"; // 10 — unchanged
doubleByRef(a);
cout << a << "\n"; // 20 — modified
vector<int> nums = {1, 2, 3, 4, 5};
cout << sumVector(nums) << "\n"; // 15
return 0;
}| Passing style | Syntax | Copies? | Caller's value changed? |
|---|---|---|---|
| By value | int x | Yes | No |
| By reference | int& x | No | Yes |
| By const reference | const int& x | No | No |
| By pointer | int* x | No (ptr is copied) | Yes (via *x) |
Default Parameters
#include <iostream>
#include <string>
using namespace std;
// Default parameter values — must be in declaration (or definition if no separate declaration)
void greet(const string& name, const string& greeting = "Hello", int times = 1) {
for (int i = 0; i < times; i++) {
cout << greeting << ", " << name << "!\n";
}
}
double power(double base, int exp = 2) {
double result = 1.0;
for (int i = 0; i < exp; i++) result *= base;
return result;
}
int main() {
greet("Alice"); // Hello, Alice!
greet("Bob", "Hi"); // Hi, Bob!
greet("Charlie", "Hey", 3); // Hey, Charlie! (×3)
cout << power(5) << "\n"; // 25 (5²)
cout << power(2, 10) << "\n"; // 1024 (2¹⁰)
return 0;
}Note: Default parameters must be specified from right to left — you cannot have a non-default parameter after a default one. Once you start using defaults, all subsequent parameters must also have defaults.
Inline Functions
#include <iostream>
using namespace std;
// The compiler may expand an inline function at the call site
// (eliminates function call overhead for tiny functions)
inline int max(int a, int b) {
return (a > b) ? a : b;
}
inline double toRadians(double degrees) {
return degrees * 3.14159265358979 / 180.0;
}
int main() {
cout << max(10, 20) << "\n"; // 20
cout << toRadians(90.0) << "\n"; // 1.5708
return 0;
}inline is a hint to the compiler, not a command — modern compilers inline functions automatically when beneficial.
Returning Multiple Values
C++ functions return one value, but you can return multiple values using:
#include <iostream>
#include <tuple>
#include <pair>
using namespace std;
// Method 1: Return a pair
pair<double, double> circleStats(double r) {
const double PI = 3.14159265358979;
return {PI * r * r, 2 * PI * r}; // {area, circumference}
}
// Method 2: Return a struct
struct DivResult { int quotient; int remainder; };
DivResult divmod(int a, int b) {
return {a / b, a % b};
}
// Method 3: Return a tuple
tuple<string, int, double> getRecord() {
return {"Alice", 30, 75000.0};
}
// Method 4: Output parameters (by reference)
void minMax(const vector<int>& v, int& minVal, int& maxVal) {
minVal = v[0];
maxVal = v[0];
for (int n : v) {
if (n < minVal) minVal = n;
if (n > maxVal) maxVal = n;
}
}
int main() {
auto [area, circ] = circleStats(5.0); // C++17 structured bindings
cout << "Area: " << area << ", Circ: " << circ << "\n";
auto [q, r] = divmod(17, 5);
cout << "17 ÷ 5 = " << q << " rem " << r << "\n";
auto [name, age, salary] = getRecord();
cout << name << " age " << age << " earns " << salary << "\n";
int lo, hi;
minMax({3, 1, 4, 1, 5, 9, 2, 6}, lo, hi);
cout << "Min: " << lo << ", Max: " << hi << "\n";
return 0;
}
// Output:
// Area: 78.5398, Circ: 31.4159
// 17 ÷ 5 = 3 rem 2
// Alice age 30 earns 75000
// Min: 1, Max: 9Lambda Functions (C++11)
Lambdas are anonymous (unnamed) functions defined inline:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
// Basic lambda: [capture](params) -> return_type { body }
auto greet = [](const string& name) {
cout << "Hi, " << name << "!\n";
};
greet("Alice"); // Hi, Alice!
// Lambda with capture (captures local variable by value)
int threshold = 50;
auto isAbove = [threshold](int x) { return x > threshold; };
cout << isAbove(60) << "\n"; // 1 (true)
cout << isAbove(40) << "\n"; // 0 (false)
// Capture by reference
int count = 0;
auto increment = [&count]() { count++; };
increment(); increment(); increment();
cout << count << "\n"; // 3
// Lambda with STL algorithms
vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};
sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; }); // Descending
for (int n : nums) cout << n << " ";
cout << "\n";
// 9 8 7 6 5 4 3 2 1
int sum = 0;
for_each(nums.begin(), nums.end(), [&sum](int n) { sum += n; });
cout << "Sum: " << sum << "\n"; // 45
return 0;
}Practical Example: Statistics Calculator
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <numeric>
using namespace std;
double mean(const vector<double>& v) {
return accumulate(v.begin(), v.end(), 0.0) / v.size();
}
double median(vector<double> v) { // Copy — sorting modifies the vector
sort(v.begin(), v.end());
int n = v.size();
return (n % 2 == 0)
? (v[n/2 - 1] + v[n/2]) / 2.0
: v[n/2];
}
double stddev(const vector<double>& v) {
double m = mean(v);
double variance = 0.0;
for (double x : v) {
variance += (x - m) * (x - m);
}
return sqrt(variance / v.size());
}
pair<double, double> range(const vector<double>& v) {
auto [minIt, maxIt] = minmax_element(v.begin(), v.end());
return {*minIt, *maxIt};
}
int main() {
vector<double> data = {4, 8, 6, 5, 3, 2, 8, 9, 2, 5};
auto [lo, hi] = range(data);
cout << "Data: ";
for (double x : data) cout << x << " ";
cout << "\n\n";
cout << "Count: " << data.size() << "\n";
cout << "Mean: " << mean(data) << "\n";
cout << "Median: " << median(data) << "\n";
cout << "Std Dev: " << stddev(data) << "\n";
cout << "Min: " << lo << "\n";
cout << "Max: " << hi << "\n";
// Count: 10
// Mean: 5.2
// Median: 5
// Std Dev: 2.27...
// Min: 2
// Max: 9
return 0;
}Summary
- Functions have a return type, name, parameters, and body; forward declarations let you define them after
main - Pass by value copies the argument — the original is safe; pass by reference lets the function modify the original
- Always use
const&for large objects (strings, vectors) you only need to read — avoids copying without allowing modification - Default parameters (right to left) let callers omit trailing arguments
- Return multiple values with
pair,tuple, astruct, or output parameters - Lambdas are anonymous functions; use
[=]to capture by value,[&]to capture by reference inlinehints the compiler to expand small functions at the call site — modern compilers decide this automatically
Next, we'll explore function overloading — defining multiple functions with the same name but different parameter lists.