Functions
Functions and Modular Programming
Section titled “Functions and Modular Programming”Functions are the building blocks of modular programming. They allow you to break your code into smaller, reusable pieces. This chapter covers everything about functions in C++, from basic syntax to advanced features like function pointers and lambda expressions.
Why Use Functions?
Section titled “Why Use Functions?”┌─────────────────────────────────────────────────────────────┐│ Benefits of Functions │├─────────────────────────────────────────────────────────────┤│ ✓ Code Reuse → Write once, use many times ││ ✓ Abstraction → Hide implementation details ││ ✓ Maintainability → Easier to modify and fix ││ ✓ Readability → Self-documenting code ││ ✓ Testability → Test each piece independently ││ ✓ Organization → Logical grouping of code │└─────────────────────────────────────────────────────────────┘Function Basics
Section titled “Function Basics”Function Declaration and Definition
Section titled “Function Declaration and Definition”// Declaration (prototype) - tells compiler about the functionint add(int a, int b);
// Definition - actual implementationint add(int a, int b) { return a + b;}Parts of a Function
Section titled “Parts of a Function”┌─────────────────────────────────────────┐│ return_type function_name(parameters) │├─────────────────────────────────────────┤│ return_type: What the function returns││ function_name: Name of the function ││ parameters: Input values (arguments) ││ body: The actual code │└─────────────────────────────────────────┘Simple Function
Section titled “Simple Function”#include <iostream>
// Function that takes no parameters and returns nothingvoid greet() { std::cout << "Hello, World!" << std::endl;}
// Function with parametersint add(int a, int b) { return a + b;}
// Function with return valuedouble calculateArea(double radius) { const double PI = 3.14159; return PI * radius * radius;}
int main() { greet(); std::cout << "5 + 3 = " << add(5, 3) << std::endl; std::cout << "Area of radius 5: " << calculateArea(5) << std::endl; return 0;}Function Parameters and Arguments
Section titled “Function Parameters and Arguments”Passing by Value
Section titled “Passing by Value”A copy of the argument is made:
void increment(int x) { x++; // Only modifies the local copy}
int main() { int num = 5; increment(num); std::cout << num; // Still 5 (original unchanged)}Passing by Reference
Section titled “Passing by Reference”The function receives the actual variable:
void increment(int& x) { x++; // Modifies the original variable}
int main() { int num = 5; increment(num); std::cout << num; // Now 6}Passing by Const Reference
Section titled “Passing by Const Reference”When you need the efficiency of reference but don’t want to modify:
void printName(const std::string& name) { // name = "Changed"; // Error: const std::cout << name << std::endl;}When to use each:
- By value: For built-in types, when you need a copy
- By reference: When you need to modify the argument
- By const reference: When you only need to read (efficient for large objects)
Return Values
Section titled “Return Values”Returning by Value
Section titled “Returning by Value”int square(int x) { return x * x;}
// The return value is copied (or moved)int result = square(5); // result is 25Returning by Reference
Section titled “Returning by Reference”int& getElement(std::vector<int>& vec, int index) { return vec[index]; // Returns reference to element}
int main() { std::vector<int> nums = {10, 20, 30}; getElement(nums, 1) = 25; // Modify through reference std::cout << nums[1]; // 25}Warning: Don’t return a reference to a local variable!
// DANGEROUS - returns reference to destroyed objectint& badFunction() { int x = 5; return x; // x is destroyed when function ends!}Returning Multiple Values
Section titled “Returning Multiple Values”// Using std::pairstd::pair<int, int> divide(int a, int b) { return {a / b, a % b};}
int main() { auto [quotient, remainder] = divide(10, 3); // quotient = 3, remainder = 1}
// Using std::tuple (C++11)std::tuple<int, int, int> getStats() { return {1, 2, 3};}
// Using structstruct Result { int quotient; int remainder;};
Result divide(int a, int b) { return {a / b, a % b};}Default Arguments
Section titled “Default Arguments”Functions can have default parameter values:
void greet(std::string name = "World") { std::cout << "Hello, " << name << "!" << std::endl;}
int main() { greet(); // Hello, World! greet("Alice"); // Hello, Alice!}Rules:
- Default arguments must be at the end
- Should be specified in declaration, not definition (or both)
// Declarationvoid process(int a, int b = 10, int c = 20);
// Definition (defaults not needed)void process(int a, int b, int c) { // ...}Function Overloading
Section titled “Function Overloading”Multiple functions can have the same name with different parameters:
// Overloaded functionsint add(int a, int b) { return a + b;}
double add(double a, double b) { return a + b;}
std::string add(const std::string& a, const std::string& b) { return a + b;}
int main() { add(1, 2); // Calls int version add(1.5, 2.5); // Calls double version add("Hello", "World"); // Calls string version}Overload Resolution: The compiler chooses the best match based on:
- Exact match
- Promotion (e.g., int to long)
- Conversion (e.g., int to double)
Inline Functions
Section titled “Inline Functions”The inline keyword suggests the compiler replace the function call with the function body:
inline int max(int a, int b) { return (a > b) ? a : b;}
int main() { // Compiler may replace with: int result = (x > y) ? x : y; int result = max(x, y);}When to use:
- Small, simple functions
- Frequently called functions
- The compiler decides whether to actually inline
Lambda Expressions (C++11)
Section titled “Lambda Expressions (C++11)”Lambdas create anonymous function objects:
// Basic lambdaauto lambda = [](int x) { return x * 2; };
int result = lambda(5); // 10
// Without storingstd::cout << [](int x) { return x * 2; }(5); // 10Lambda Syntax
Section titled “Lambda Syntax”[ captures ] ( parameters ) -> return_type { body }Captures
Section titled “Captures”int x = 10;
// Capture by value (copy)auto f1 = [x]() { return x; };
// Capture by referenceauto f2 = [&x]() { x = 20; };
// Capture all by valueauto f3 = [=]() { return x; };
// Capture all by referenceauto f4 = [&]() { return x; };
// C++14: capture expressionauto f5 = [val = x * 2]() { return val; };Examples
Section titled “Examples”#include <algorithm>#include <vector>
std::vector<int> nums = {5, 2, 8, 1, 9};
// Find first number > 5auto it = std::find_if(nums.begin(), nums.end(), [](int n) { return n > 5; });
// Sort with custom comparatorstd::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; });
// for_each with lambdastd::for_each(nums.begin(), nums.end(), [](int n) { std::cout << n << " "; });Function Pointers
Section titled “Function Pointers”Pointers to functions:
int add(int a, int b) { return a + b;}
int (*funcPtr)(int, int) = add;
int result = funcPtr(5, 3); // 8Using with std::function (C++11):
#include <functional>
std::function<int(int, int)> operation = add;Recursive Functions
Section titled “Recursive Functions”Functions that call themselves:
// Factorialint factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);}
// Fibonacciint fibonacci(int n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2);}Tail Recursion:
// Tail-recursive factorial (compiler may optimize)int factorialTail(int n, int accumulator = 1) { if (n <= 1) return accumulator; return factorialTail(n - 1, n * accumulator);}Best Practices
Section titled “Best Practices”1. Use Meaningful Names
Section titled “1. Use Meaningful Names”// Badvoid doIt(int x);
// Goodvoid calculateTotal(int quantity);2. Keep Functions Short
Section titled “2. Keep Functions Short”Each function should do one thing well.
3. Use const Correctly
Section titled “3. Use const Correctly”// Member function that doesn't modify stateclass Calculator {public: int add(int a, int b) const { // const member function return a + b; }};4. Document Functions
Section titled “4. Document Functions”/** * @brief Calculates the factorial of a non-negative integer * @param n The number to calculate factorial for (must be >= 0) * @return The factorial result, or 1 if n is 0 * @throws std::invalid_argument if n is negative */int factorial(int n);5. Avoid Global Variables
Section titled “5. Avoid Global Variables”// Badint globalCount;void increment() { globalCount++; }
// Goodint increment(int count) { return count + 1; }Complete Example: Calculator
Section titled “Complete Example: Calculator”#include <iostream>#include <vector>#include <string>
class Calculator {private: double memory = 0;
public: double add(double a, double b) { return a + b; } double subtract(double a, double b) { return a - b; } double multiply(double a, double b) { return a * b; } double divide(double a, double b) { if (b == 0) { throw std::invalid_argument("Cannot divide by zero"); } return a / b; }
void storeInMemory(double value) { memory = value; } double recallMemory() { return memory; } void clearMemory() { memory = 0; }};
int main() { Calculator calc;
std::cout << "5 + 3 = " << calc.add(5, 3) << std::endl; std::cout << "10 - 4 = " << calc.subtract(10, 4) << std::endl; std::cout << "6 * 7 = " << calc.multiply(6, 7) << std::endl; std::cout << "20 / 4 = " << calc.divide(20, 4) << std::endl;
// Using memory calc.storeInMemory(100); std::cout << "Memory: " << calc.recallMemory() << std::endl;
return 0;}Key Takeaways
Section titled “Key Takeaways”- Functions are reusable blocks of code that perform specific tasks
- Use pass-by-value for small types, pass-by-reference for modification
- Use const reference for read-only large objects
- Function overloading allows multiple functions with the same name
- Lambda expressions create anonymous functions (very useful with STL)
- Keep functions short and focused on a single task
- Use meaningful names and document your functions
- Prefer returning by value; use references when necessary
Next Steps
Section titled “Next Steps”Now let’s dive into Object-Oriented Programming with classes and objects.
Next Chapter: 02_oop/08_classes_objects.md - Classes and Objects