C++: Functions
Learn to declare, define, and call functions, pass arguments in multiple ways, and use advanced features like overloading, recursion, lambdas, and templates.
Function Declaration and Definition
A declaration (prototype) tells the compiler a function's name, return type, and parameters before the body is written. A definition provides the full body. If the definition appears above the call site, no separate declaration is needed.
Declaration vs Definition
A function can be declared many times but defined exactly once, defining it twice in the same file is a compile error.
- Declaration: returnType name(paramTypes);, ends with semicolon, no body
- Definition: returnType name(params) { body }, provides the implementation
- Prototypes are typically placed at the top of the file or in a header (.h)
- Parameter names are optional in declarations; types alone are sufficient
Declaration and Definition
C++The prototype lets main() call add() before the definition appears.
Calling a Function and the return Statement
A function call passes arguments to the function and receives the returned value. The return statement immediately exits the function and sends a value back to the caller. A non-void function must return a value on every execution path.
Function Call and return
A function call is an expression, store its result in a variable or use it directly inside another expression.
- Syntax: functionName(arg1, arg2)
- A function can have multiple return statements; the first reached exits the function
- Early return: use return inside an if block to handle edge cases before the main logic
- Reaching the end of a non-void function without a return is undefined behavior
Early Return
C++The first return reached exits the function immediately.
void Functions
A void function performs an action but returns no value. Use a bare return; to exit early, or simply let the function run to its closing brace.
void
Use void for side-effect operations, printing, modifying objects, writing files, where a return value would be meaningless.
- Declared with void as the return type: void printLine(string msg)
- Called as a standalone statement: printLine("Hello");
- Cannot use the call inside an expression, it produces no value
- An early return; (no value) is valid to exit before the end of the body
void Function
C++printStars performs output and returns nothing.
Pass by Value
When you pass by value the function receives a copy of the argument. Changes inside the function affect only the copy, the original variable is untouched.
Pass by Value
Use pass-by-value for small, cheap-to-copy types like int, double, and char.
- A new copy is created on the function's stack frame
- Modifying the parameter has no effect on the caller's variable
- Safe but slow for large objects, prefer const& for those
Pass by Value
C++The original x is unchanged because doubleIt received a copy.
Pass by Reference
Pass by reference gives the function a direct alias to the caller's variable. Changes inside the function modify the original. Use it when a function must update the caller's data or when copying is expensive.
Pass by Reference (&)
References are the idiomatic C++ way to let a function modify the caller's variable without using pointers.
- Syntax: void func(int& x), the & binds to the parameter name
- No copy is made; the parameter is another name for the same memory
- References cannot be null, safer than pointers for this purpose
- Use const& for large read-only inputs: void func(const string& s)
Pass by Reference: Swap
C++swap() modifies x and y directly through references.
Pass by Pointer
Passing a pointer gives the function the memory address of the variable. The function dereferences the pointer to read or modify the original. Pointers can be null; references cannot.
Pass by Pointer (*)
Prefer references over pointers in modern C++; use pointers when nullptr is a meaningful state or when working with C-style APIs.
- Syntax: void func(int* p), caller passes &variable
- Dereference inside the function with *p to read or write the value
- Always guard against nullptr before dereferencing
Pass by Pointer
C++Pass the address with & and dereference inside the function with *.
Default Arguments
Default arguments let callers omit trailing parameters. The compiler substitutes the default when no argument is supplied. Defaults must be assigned right-to-left in the parameter list.
Default Arguments
Specify defaults in the declaration, never in both the declaration and definition, or you will get a compile error.
- Syntax: void func(int a, int b = 10, int c = 20)
- Defaults must be on the rightmost parameters, you cannot skip a middle one
- Callers can override any default by supplying an argument explicitly
Default Arguments
C++Omit the title to use the default, or supply one to override it.
Constant Parameters
Adding const to a parameter documents and enforces that the function will not modify that argument. The most useful form is const T&: no copy, no modification.
const Parameters
Use const& for any parameter that is read-only and non-trivial to copy, it is both safe and efficient.
- const int x: the copy cannot be changed inside the function
- const int& x: no copy, no modification, standard for large read-only inputs
- const int* p: pointer to a value that cannot be changed through p
const Reference Parameter
C++No copy is made and the string cannot be modified inside the function.
Function Overloading
Function overloading lets you define multiple functions with the same name as long as their parameter lists differ in type or count. The compiler picks the correct version at the call site.
Function Overloading
Overloading creates one coherent name for the same conceptual operation across different types, the compiler resolves which version to call at compile time.
- Functions are distinct when their parameter types or count differ
- Return type alone does not distinguish overloads, the compiler will error
- The compiler selects the best match among candidates
Function Overloading
C++The compiler selects the right print() based on the argument type.
Recursion
A recursive function calls itself to solve a smaller version of the same problem. Every recursive function needs a base case that stops the recursion and a recursive case that moves toward it.
Recursion
Without a base case the function calls itself forever and causes a stack overflow, always identify the stopping condition first.
- Base case: the simplest input answered directly without further recursion
- Recursive case: reduces the problem and calls itself with a smaller input
- Each call adds a stack frame, deep recursion can overflow the stack
- Use iteration when possible; reach for recursion when it makes logic significantly clearer
Recursion: Factorial
C++factorial(5) calls factorial(4)... down to the base case, then multiplies back up.
Inline Functions
The inline keyword hints to the compiler to substitute the function's body at the call site, removing call overhead. Modern compilers inline aggressively on their own, so this keyword is mainly useful for small utility functions in headers.
inline
Use inline for very small, frequently-called functions (1-3 lines); the compiler may ignore the hint or inline without it.
- Eliminates function-call overhead for tiny hot-path functions
- The definition must be visible at the call site, typically placed in a header
- Overuse bloats the binary, do not inline large functions
Inline Function
C++A one-liner is a good candidate for inlining, the compiler replaces the call with the body.
Friend Functions
A friend function is declared inside a class but defined outside it. It is not a member, yet it has access to the class's private and protected data.
friend
Grant friend access sparingly, it breaks encapsulation. Reserve it for cases where member access genuinely cannot work, such as symmetric operator overloads.
- Declared inside the class with the friend keyword
- Defined outside the class like a regular free function (no ClassName:: prefix)
- Has access to private and protected members of the granting class
- Friend relationship is not inherited or mutual
Friend Function
C++showWidth is not a member but can still read Box's private width.
Lambda Functions (C++11)
A lambda is an anonymous function defined inline at the point of use. Lambdas are ideal for short callbacks and predicates passed to standard-library algorithms.
Lambda Syntax
The capture list [] controls which variables from the surrounding scope the lambda body can see.
- Syntax: [capture](params) { body }
- []: capture nothing, [=]: all by value, [&]: all by reference
- [x, &y]: capture x by value, y by reference
- Return type is deduced automatically; specify with -> type if needed
Lambda as Sort Comparator
C++An anonymous function sorts the vector without needing a named function.
Function Templates
A function template defines a generic function parameterized by a type. The compiler generates the concrete function for each type it is called with, eliminating repetitive overloads.
Function Templates
Templates are resolved entirely at compile time, there is no runtime overhead compared to writing a separate function for each type.
- Syntax: template<typename T> T funcName(T a, T b) { }
- T is a placeholder replaced by the actual type at each call site
- You can have multiple type parameters: template<typename T, typename U>
- Specify the type explicitly when the compiler cannot deduce it: maxOf<double>(3, 4.5)
Function Template
C++One template definition works for int, double, and char, the compiler generates each version.
Knowledge Check
1. What is a function prototype?
2. What happens to the original variable when you pass by value?
3. Which syntax declares a pass-by-reference parameter?
4. What makes two overloaded functions distinct to the compiler?
5. What is the base case in a recursive function?
6. What does a lambda capture list [] control?
7. What is the advantage of a function template over writing separate overloads?