Functions

What are Functions?

Functions are reusable blocks of code that perform specific tasks. They help organize code, reduce repetition, and make programs more modular and maintainable.

Function Anatomy

return_type function_name (parameters) {
// function body
return value;
}

Basic Function Declaration

Here's how to declare and use basic functions:

Simple Functions

#include <iostream>
using namespace std;

// Function declaration (prototype)
void greet();
int add(int a, int b);
double calculateArea(double radius);

int main() {
    greet();
    
    int sum = add(5, 3);
    cout << "Sum: " << sum << endl;
    
    double area = calculateArea(2.5);
    cout << "Area: " << area << endl;
    
    return 0;
}

// Function definitions
void greet() {
    cout << "Hello, World!" << endl;
}

int add(int a, int b) {
    return a + b;
}

double calculateArea(double radius) {
    const double PI = 3.14159;
    return PI * radius * radius;
}

Function Parameters

Functions can accept parameters in different ways:

Parameter Passing

#include <iostream>
using namespace std;

// Pass by value
void passByValue(int x) {
    x = 100;  // Only changes local copy
    cout << "Inside function: " << x << endl;
}

// Pass by reference
void passByReference(int& x) {
    x = 100;  // Changes original variable
    cout << "Inside function: " << x << endl;
}

// Pass by pointer
void passByPointer(int* x) {
    *x = 100;  // Changes original variable
    cout << "Inside function: " << *x << endl;
}

// Const parameters
void printValue(const int& value) {
    cout << "Value: " << value << endl;
    // value = 50;  // Error: cannot modify const parameter
}

int main() {
    int num = 10;
    
    cout << "Original value: " << num << endl;
    
    passByValue(num);
    cout << "After pass by value: " << num << endl;
    
    passByReference(num);
    cout << "After pass by reference: " << num << endl;
    
    num = 20;
    passByPointer(&num);
    cout << "After pass by pointer: " << num << endl;
    
    printValue(num);
    
    return 0;
}

Function Overloading

C++ allows multiple functions with the same name but different parameters:

Function Overloading Example

#include <iostream>
using namespace std;

// Overloaded functions
int multiply(int a, int b) {
    return a * b;
}

double multiply(double a, double b) {
    return a * b;
}

int multiply(int a, int b, int c) {
    return a * b * c;
}

// Display functions for different types
void display(int value) {
    cout << "Integer: " << value << endl;
}

void display(double value) {
    cout << "Double: " << value << endl;
}

void display(const string& value) {
    cout << "String: " << value << endl;
}

int main() {
    // Compiler chooses the right function based on arguments
    cout << multiply(3, 4) << endl;        // Calls int version
    cout << multiply(3.5, 2.5) << endl;    // Calls double version
    cout << multiply(2, 3, 4) << endl;     // Calls three-parameter version
    
    display(42);
    display(3.14159);
    display("Hello World");
    
    return 0;
}

Default Parameters

Functions can have default parameter values:

Default Parameters Example

#include <iostream>
using namespace std;

// Function with default parameters
void printInfo(const string& name, int age = 18, const string& city = "Unknown") {
    cout << "Name: " << name << endl;
    cout << "Age: " << age << endl;
    cout << "City: " << city << endl;
    cout << "---" << endl;
}

// Calculate power with default exponent
double power(double base, int exponent = 2) {
    double result = 1;
    for (int i = 0; i < exponent; i++) {
        result *= base;
    }
    return result;
}

int main() {
    // Different ways to call functions with default parameters
    printInfo("Alice");                    // Uses default age and city
    printInfo("Bob", 25);                  // Uses default city
    printInfo("Charlie", 30, "New York");  // No defaults used
    
    cout << "2^2 = " << power(2) << endl;      // Uses default exponent (2)
    cout << "2^3 = " << power(2, 3) << endl;   // Specifies exponent
    cout << "5^4 = " << power(5, 4) << endl;   // Specifies exponent
    
    return 0;
}

Recursive Functions

Functions can call themselves to solve problems recursively:

Recursion Examples

#include <iostream>
using namespace std;

// Calculate factorial recursively
long long factorial(int n) {
    if (n <= 1) {
        return 1;  // Base case
    }
    return n * factorial(n - 1);  // Recursive case
}

// Calculate Fibonacci number recursively
long long fibonacci(int n) {
    if (n <= 1) {
        return n;  // Base cases: fib(0) = 0, fib(1) = 1
    }
    return fibonacci(n - 1) + fibonacci(n - 2);  // Recursive case
}

// Calculate sum of digits recursively
int sumOfDigits(int n) {
    if (n == 0) {
        return 0;  // Base case
    }
    return (n % 10) + sumOfDigits(n / 10);  // Recursive case
}

int main() {
    cout << "Factorial of 5: " << factorial(5) << endl;
    cout << "Factorial of 10: " << factorial(10) << endl;
    
    cout << "\nFibonacci sequence:" << endl;
    for (int i = 0; i < 10; i++) {
        cout << fibonacci(i) << " ";
    }
    cout << endl;
    
    cout << "\nSum of digits in 12345: " << sumOfDigits(12345) << endl;
    cout << "Sum of digits in 987: " << sumOfDigits(987) << endl;
    
    return 0;
}

Lambda Functions (C++11)

Lambda functions provide a concise way to define anonymous functions:

Lambda Functions

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    // Basic lambda function
    auto greet = []() {
        cout << "Hello from lambda!" << endl;
    };
    greet();
    
    // Lambda with parameters
    auto add = [](int a, int b) {
        return a + b;
    };
    cout << "Sum: " << add(5, 3) << endl;
    
    // Lambda with capture
    int multiplier = 10;
    auto multiply = [multiplier](int x) {
        return x * multiplier;
    };
    cout << "5 * 10 = " << multiply(5) << endl;
    
    // Using lambda with STL algorithms
    vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Count even numbers
    int evenCount = count_if(numbers.begin(), numbers.end(), 
                            [](int n) { return n % 2 == 0; });
    cout << "Even numbers count: " << evenCount << endl;
    
    // Transform all numbers (multiply by 2)
    transform(numbers.begin(), numbers.end(), numbers.begin(),
              [](int n) { return n * 2; });
    
    cout << "Doubled numbers: ";
    for (int n : numbers) {
        cout << n << " ";
    }
    cout << endl;
    
    return 0;
}

Function Best Practices

Single Responsibility

Each function should do one thing and do it well

Meaningful Names

Use descriptive names that clearly indicate what the function does

Keep Functions Small

Aim for functions that fit on one screen (20-30 lines max)

Use const Correctly

Mark parameters const when they shouldn't be modified

Avoid Global Variables

Pass data through parameters instead of using global variables

Handle Edge Cases

Consider and handle invalid inputs and edge cases