Contents

Functions

Functions in C++

A function is a set of statements designed to perform a specific task and is executed only when called. Functions allow you to modularize code, reducing redundancy and improving clarity. By encapsulating common tasks within functions, we avoid repeating the same logic multiple times, making the code more manageable and easier to maintain.

Syntax:

				
					void function(int size)
{
    int array[size];
    // code to manipulate the array
}

				
			

Example:

				
					// C++ Program to demonstrate the working of a function
#include <iostream>
using namespace std;

// Function that takes two integers as parameters
// and returns the larger of the two
int max(int x, int y) {
    return (x > y) ? x : y;
}

int main() {
    int a = 15, b = 25;
    
    // Calling the max function
    int result = max(a, b);

    cout << "The maximum value is " << result;
    return 0;
}
				
			

Output:

				
					The maximum value is 25
				
			
Why Do We Need Functions?
  • Code Reusability: Avoid redundancy by reusing functions across the program.
  • Modularity: Code becomes more organized and easier to understand when divided into functions.
  • Abstraction: Using functions hides internal details, making complex tasks easier to use through simple function calls.
Function Declaration

A function declaration tells the compiler about the function’s name, parameters, and return type. Here is an example:

				
					// Function declarations
int add(int, int);         // Function with two integer parameters
float subtract(float, int); // Function with float and int parameters
char* reverseString(char*); // Function that returns a char pointer
				
			
Types of Functions

1. User-defined Functions: Functions created by the user to perform specific tasks.
2. Library Functions: Predefined functions in C++ that you can use directly, such as sqrt(), strlen(), etc

Passing Parameters to Functions

There are two common ways to pass parameters to functions:

  • Pass by Value: A copy of the variable is passed. Modifications inside the function do not affect the original variable.
  • Pass by Reference: The function receives the memory address of the variable, and changes made inside the function affect the original variable.
Function Example: Pass by Value
				
					#include <iostream>
using namespace std;

void modify(int x) {
    x = 50;  // Changes won't affect the original variable
}

int main() {
    int num = 10;
    modify(num);
    cout << "Value of num: " << num;  // Outputs 10
    return 0;
}
				
			

Function Example: Pass by Reference

				
					#include <iostream>
using namespace std;

void modify(int &x) {
    x = 50;  // Changes will affect the original variable
}

int main() {
    int num = 10;
    modify(num);
    cout << "Value of num: " << num;  // Outputs 50
    return 0;
}

				
			

Function Returning a String example: 

				
					#include <iostream>
#include <string>

std::string greet() {
    return "Hello, C++!";
}

int main() {
    std::string message = greet();
    cout << message;
    return 0;
}

				
			

Output:

				
					Hello, C++!

				
			

Function Returning a Pointer example:

				
					#include <iostream>
using namespace std;

int* createArray(int size) {
    int* arr = new int[size];  // Dynamically allocate memory
    for (int i = 0; i < size; ++i) {
        arr[i] = i * 2;
    }
    return arr;
}

int main() {
    int* arr = createArray(5);
    for (int i = 0; i < 5; ++i) {
        cout << arr[i] << " ";  // Outputs: 0 2 4 6 8
    }
    delete[] arr;  // Free allocated memory
    return 0;
}

				
			

Output:

				
					0 2 4 6 8 

				
			

Callback Function Example:

				
					#include <iostream>
using namespace std;

typedef void (*Callback)();  // Define a callback function type

void action(Callback callback) {
    cout << "Performing action...\n";
    callback();  // Call the passed callback function
}

void myCallback() {
    cout << "Callback function executed!";
}

int main() {
    action(myCallback);
    return 0;
}

				
			

Output:

				
					Performing action...
Callback function executed!

				
			
Differences Between Call by Value and Call by Reference

Call by Value

Call by Reference

A copy of the value is passed.

The reference (memory address) is passed.

Changes do not affect the original.

Changes affect the original.

Actual and formal parameters are stored at different locations.

Actual and formal parameters share the same location.

return statement

The return statement in C++ transfers control back to the function that invoked the current function. Once the return statement is executed, the function ends and any subsequent code in that function is ignored. For non-void functions, a return statement must return a value, while in void functions, the return statement can be omitted or used without returning any value.

Syntax:

				
					return [expression];
				
			
Functions Without a Return Value (void Functions)

In C++, if a function is defined with a void return type, it does not return any value. The return statement is optional and can be used to exit the function early, but it should not return any value.

Example:

				
					// C++ Program demonstrating a void function without a return statement
#include <iostream>
using namespace std;

void displayMessage() {
    cout << "Hello, World!";
}

int main() {
    displayMessage();  // Function call
    return 0;
}

				
			

Output:

				
					Hello, World!
				
			
Functions With return in a Void Function:

In a void function, the return statement can be used simply to exit the function early.

Example:

				
					// C++ Program using return in a void function
#include <iostream>
using namespace std;

void checkValue(int x) {
    if (x < 0) {
        return;  // Exit the function early if x is negative
    }
    cout << "The value is non-negative: " << x << endl;
}

int main() {
    checkValue(-5);  // Function call
    checkValue(10);  // Function call
    return 0;
}

				
			

Output

				
					The value is non-negative: 10
				
			

Default Arguments in C++

In C++, a default argument is a value that is automatically provided by the compiler when a calling function does not specify a value for the argument. If a value is passed for the argument, it overrides the default value.

Example : Default Arguments in a Function

The following C++ example demonstrates the use of default arguments. We don’t need to write multiple functions for different numbers of arguments; one function can handle various cases by using default values for some parameters.

				
					// C++ Program to demonstrate Default Arguments
#include <iostream>
using namespace std;

// Function with default arguments
// It can be called with 2, 3, or 4 arguments.
int sum(int x, int y, int z = 0, int w = 0) {
    return (x + y + z + w);
}

// Driver Code
int main() {
    // Case 1: Passing two arguments
    cout << sum(10, 15) << endl;

    // Case 2: Passing three arguments
    cout << sum(10, 15, 25) << endl;

    // Case 3: Passing four arguments
    cout << sum(10, 15, 25, 30) << endl;

    return 0;
}

				
			

Output:

				
					25
50
80

				
			
Key Points to Remember:

1. Inlining is a suggestion, not a directive. The compiler may choose to ignore the inline request.

2. The compiler may not inline the function if it contains:

  • Loops (for, while, or do-while)
  • Static variables
  • Recursive calls
  • A return type other than void and lacks a return statement
  • Complex constructs such as switch or goto
Why Use Inline Functions?

When a function is called, there’s an overhead for:

1. Storing the return address
2. Passing function arguments
3. Transferring control to the function’s code
4. Returning the result from the function

For small and frequently called functions, this overhead might exceed the time it takes to execute the function itself. By inlining such functions, you avoid the cost of the function call and may improve performance. Inline functions are beneficial for small, simple functions where function call overhead might be significant relative to execution time.

Advantages of Inline Functions:
  • Reduced Function Call Overhead: No need to jump to a new memory address for function execution.

  • No Stack Push/Pop Overhead: Parameters don’t need to be pushed or popped from the stack.

  • No Return Overhead: Control doesn’t need to be returned to the caller.

  • Potential for Context-Specific Optimization: The compiler can perform optimizations that wouldn’t be possible with regular function calls, as the function’s code is available at the point of use.

  • Smaller Code Size for Embedded Systems: Inlining small functions can sometimes produce more compact code in certain situations (e.g., embedded systems).

Example:

				
					// Inline function to calculate the square of a number
#include <iostream>
using namespace std;

inline int square(int x) {
    return x * x;
}

int main() {
    int num = 5;

    // Using the inline function
    cout << "Square of " << num << " is " << square(num) << endl;

    // Another usage of the inline function
    cout << "Square of 10 is " << square(10) << endl;

    return 0;
}

				
			

Output:

				
					Square of 5 is 25
Square of 10 is 100

				
			

Lambda Expressions

C++11 introduced lambda expressions, which allow you to write inline functions for short snippets of code without giving them a name. These functions are useful for situations where you won’t reuse the code, and therefore don’t need to define a separate function.

Syntax:

				
					// C++ program demonstrating lambda expressions
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric> // for accumulate

using namespace std;

// Function to print vector
void printVector(vector<int> v)
{
    // Lambda expression to print vector elements
    for_each(v.begin(), v.end(), [](int i)
    {
        cout << i << " ";
    });
    cout << endl;
}

int main()
{
    vector<int> v {4, 1, 3, 5, 2, 3, 1, 7};

    printVector(v);

    // Finding the first number greater than 4
    auto p = find_if(v.begin(), v.end(), [](int i)
    {
        return i > 4;
    });
    cout << "First number greater than 4 is : " << *p << endl;

    // Sorting the vector in descending order
    sort(v.begin(), v.end(), [](int a, int b) -> bool
    {
        return a > b;
    });

    printVector(v);

    // Counting numbers greater than or equal to 5
    int count_5 = count_if(v.begin(), v.end(), [](int a)
    {
        return a >= 5;
    });
    cout << "The number of elements greater than or equal to 5 is : "
         << count_5 << endl;

    // Removing duplicate elements
    auto unique_end = unique(v.begin(), v.end(), [](int a, int b)
    {
        return a == b;
    });
    v.resize(distance(v.begin(), unique_end));
    printVector(v);

    // Calculating factorial using accumulate
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int factorial = accumulate(arr, arr + 10, 1, [](int i, int j)
    {
        return i * j;
    });
    cout << "Factorial of 10 is : " << factorial << endl;

    // Storing a lambda in a variable to compute square
    auto square = [](int i)
    {
        return i * i;
    };
    cout << "Square of 5 is : " << square(5) << endl;

    return 0;
}

				
			

Output:

				
					4 1 3 5 2 3 1 7 
First number greater than 4 is : 5
7 5 4 3 3 2 1 1 
The number of elements greater than or equal to 5 is : 2
7 5 4 3 2 1 
Factorial of 10 is : 3628800
Square of 5 is : 25