Functions in C++ Programming

Functions in C++

Functions are the building blocks of any programming language. In C++, a function is a block of code that performs a specific task. Think of it as a mini-program within your main program, designed to execute a particular operation. Functions are like building blocks that help break down complex problems into manageable chunks, making your code more readable and modular.

Understanding functions is like unlocking the door to a world of organized and efficient coding. In this article, we’ll take a closer look at what functions are, why they are essential, and how you can leverage them to enhance your C++ programming skills.

“A Function is a self contained block of code with a specific purpose. It has a name that is used to identify and call it for execution. The function name is global, but it is not necessarily unique in C++.”

Ivor Hoprton

Table of Contents

Declaration of a Function

The standard form of declaration of a function is:

return_type function_name(parameters list)
{
    //body of the function
}

Anatomy of a Function

There are two main parts of the function, function header and the function body. Look at the following sa

// Return_type function_name(Parameter_list) {
//     // Function body
//     // Code to perform the task
//     return result; // (optional, depends on the return type)
// }

Function Header

In the first line of the C++ code below has three main parts which comprises a function header.

  1. The name of the function i.e. function_name()
  2. The parameters of the function enclosed in parenthesis ( Parameter_list )
  3. Return value type i.e. int

Function Body

Function body is the actual code that performs the specified task. This is enclosed within curly braces {}.

#include <iostream>

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

int main() {
    // Function call
    int result = add(5, 7);
    
    // Output the result
    std::cout << "The sum is: " << result << std::endl;

    return 0;
}

Function Prototype

A function prototype in C++ is a declaration of a function that tells the compiler about the function’s name, return type, and the types of its parameters. It provides essential information to the compiler which allows it to understand how the function should be called and what values it expects.

The general syntax of a function prototype is:

return_type function_name(parameter_type1, parameter_type2, …);

Using function prototypes is important in larger programs or when functions are defined after they are called. By providing the compiler with a prototype, you inform it about the existence and signature of the function, enabling it to catch potential errors related to function calls and parameter mismatches.

#include <iostream>

// Function prototype
int add(int a, int b);

int main() {
    // Function call
    int result = add(5, 7);

    // Output the result
    std::cout << "The sum is: " << result << std::endl;

    return 0;
}

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

In this example, the function prototype int add(int a, int b); informs the compiler about the add function’s signature before it’s actually defined in the code. This allows the main function to call add without any issues.

How does Prototyping Work?

Prototyping is a powerful technique in C++ that involves declaring the structure of a function before its actual implementation. To understand its impact, let’s experiment with a simple program. Take a look at the code snippet below:

#include <iostream>

// Function prototype
void printValues(double a, int b, long c);

int main() {
    double x = 12.2;
    int y = 13;
    long z = 12345;

    // Try changing the parameters and observe the compiler's response
    printValues(x, y, z);  // Original parameters

    // Experiment with different parameter changes
    // printValues(12.0, 13);   // Error: Not enough arguments
    // printValues(&x, y, z);   // Error: Type mismatch with the address of 'x'
    
    return 0;
}

// Function definition
void printValues(double a, int b, long c) {
    std::cout << "Values: " << a << ", " << b << ", " << c << std::endl;
}

In this example, the printValues function prototype is declared before the main function. The parameters (double, int, long) signify the types of values the function expects. By experimenting with different parameter changes, you can observe how the compiler responds. For instance, providing too few or mismatched types of arguments will result in error messages, highlighting the importance of accurate function prototypes.

To reinforce this understanding, try changing the return type in the prototype and notice how the compiler guides you through adjusting both the prototype and the function header.

Further Explanation on Prototyping

Let’s examine the next example program for a little more information on prototyping.

#include <iostream>

void do_stuff(int, float, char);

int main() {
    int arm = 2;
    float foot = 1000.0;
    char lookers = 65;

    // Function calls with different arguments
    do_stuff(3, 12.0, 67);
    do_stuff(arm, foot, lookers);

    return 0;
}

// Function definition with explanatory comments
void do_stuff(int wings, // Number of wings
              float feet, // Number of feet
              char eyes)  // Number of eyes
{
    // Output information based on function parameters
    std::cout << "There are " << wings << " wings." << "\n";
    std::cout << "There are " << feet << " feet." << "\n";
    std::cout << "There are " << eyes << " eyes." << "\n\n";
}

In this example, the function prototype in line 4 lacks variable names. While comments alongside parameters in the function header can improve clarity, it’s crucial to rely on well-chosen variable names for effective communication within your code. Strive to use comments judiciously and prioritize clear and meaningful variable naming conventions for optimal code readability.

Pass by Reference

Passing variables to functions usually involves creating copies, leaving the original values untouched. However, there exists a powerful construct called “pass by reference,” a feature not available in ANSI-C. Let’s explore this concept through an example program.

#include <iostream>
#include <stdio.h>

void fiddle(int in1, int &in2);

int main() {
    int count = 7, index = 12;

    // Display initial values
    std::cout << "The values are " << count << " " << index << "\n";

    // Call function with pass by reference
    fiddle(count, index);

    // Display values after function call
    std::cout << "The values are " << count << " " << index << "\n";

    return 0;
}

// Function definition using pass by reference
void fiddle(int in1, int &in2) {
    in1 = in1 + 100;
    in2 = in2 + 100;

    // Display values within the function
    std::cout << "The values are " << in1 << " " << in2 << "\n";
}

Upon executing this program, you’ll notice that the values of the first variable (count) get changed within the function but revert to their original state upon returning to the main program. On the other hand, the second variable (index) undergoes a transformation in the function, and this change is reflected back into the main program.

To achieve pass by reference, observe the function prototype in line 4, where the second variable is denoted with an ampersand (&). This instructs the compiler to treat the variable as if a pointer to the original variable were passed, making modifications directly in the main program’s variable. The function itself uses the referenced variable (in2) just like any other variable, but it manipulates the original variable from the main program.

If you prefer a cleaner prototype without variable names, you can write it as follows:

void fiddle(int, int&);

The Default Parameters

In C++, default parameters offer a convenient way to enhance the flexibility of function calls. Let’s explore an example program to understand how default parameters work and how they can simplify code:

#include <iostream.h>
#include <stdio.h>

int get_volume(int length, int width = 2, int height = 3);

int main() {
    int x = 10, y = 12, z = 15;

    // Displaying box data with different parameter combinations
    cout << "Some box data is " << get_volume(x, y, z) << "\n";
    cout << "Some box data is " << get_volume(x, y) << "\n";
    cout << "Some box data is " << get_volume(x) << "\n";

    cout << "Some box data is ";
    cout << get_volume(x, 7) << "\n";
    cout << "Some box data is ";
    cout << get_volume(5, 5, 5) << "\n";

    return 0;
}

// Function definition with default parameters
int get_volume(int length, int width, int height) {
    printf("%4d %4d %4d ", length, width, height);
    return length * width * height;
}

In this example, the get_volume function has default values assigned to the width and height parameters in its prototype. This means that if these parameters are not explicitly provided during a function call, the default values (2 for width and 3 for height) will be used. Let’s break down the function calls in the main function:

  • Line 11: All three parameters specified.
  • Line 12: Only two parameters specified, using the default value for height.
  • Line 13: Only one parameter specified, defaulting both width and height.

The output reflects the values passed to the function, with the reversed order explained later in the program.

Some rules to keep in mind:

  1. Once a parameter is given a default value, all subsequent parameters must also have default values.
  2. Default values must match the correct types.
  3. Default values can be specified in either the prototype or the function header, but not both.

It’s advisable to provide default values in the prototype for clarity and consistency, especially when venturing into object-oriented programming techniques. Default parameters are a powerful tool, enabling you to create more flexible and readable code by allowing some parameters to be omitted during function calls. Happy coding!

Variable Arguments to a Function

There are occasions when your C++ function needs to accept a variable number of arguments. While this practice should be approached with caution due to its potential to introduce complexity, it can be a powerful tool when used judiciously. The following example program illustrates the use of a variable number of arguments in a function call:

#include <iostream.h>
#include <stdarg.h>

void display_var(int number, ...);

int main() {
    int index = 5;
    int one = 1, two = 2;

    // Displaying variable number of arguments
    display_var(one, index);
    display_var(3, index, index + two, index + one);
    display_var(two, 7, 3);

    return 0;
}

void display_var(int number, ...) {
    va_list param_pt;
    va_start(param_pt, number); // Call the setup macro

    cout << "The parameters are ";
    for (int index = 0; index < number; index++)
        cout << va_arg(param_pt, int) << " "; // Extract a parameter
    cout << "\n";

    va_end(param_pt); // Closing macro
}

In this example, the display_var function is declared with the first parameter as an integer (number) indicates the number of subsequent arguments to expect. The ellipsis (...) in the parameter list allows for a variable number of arguments without enforcing strong type checking beyond the first parameter.

The main function showcases three different calls to display_var, each with a different number of parameters. The system accommodates these differences, and the function successfully processes the arguments provided.

Keep in mind that while this capability offers flexibility, it also relinquishes the robust type checking normally enforced by C++. Therefore, it’s imperative to ensure that the correct types are used in the function call, as the compiler will not perform type checking beyond the first parameter.

Function Name Overloading

In C++, function name overloading empowers programmers to create multiple functions with the same name, each tailored to handle different types and numbers of parameters. The example program below illustrates the versatility of function name overloading:

#include <iostream.h>

// Keyword 'overload' (optional in C++ version 1.2)
overload do_stuff;

// Function prototypes with overloading
int do_stuff(const int);
int do_stuff(float);
float do_stuff(const float, float);

int main() {
    int index = 12;
    float length = 14.33;
    float height = 34.33;

    // Displaying results of overloaded function calls
    cout << "12 squared is " << do_stuff(index) << "\n";
    cout << "24 squared is " << do_stuff(2 * index) << "\n";
    cout << "Three lengths is " << do_stuff(length) << "\n";
    cout << "Three heights is " << do_stuff(height) << "\n";
    cout << "The average is " << do_stuff(length, height) << "\n";

    return 0;
}

// Overloaded function: squares an integer
int do_stuff(const int in_value) {
    return in_value * in_value;
}

// Overloaded function: triples a float and returns an integer
int do_stuff(float in_value) {
    return (int)(3.0 * in_value);
}

// Overloaded function: averages two floats
float do_stuff(const float in1, float in2) {
    return (in1 + in2) / 2.0;
}

In this example, three functions coexist with the same name, do_stuff, each serving a unique purpose based on the types and number of parameters. The program’s output demonstrates the successful execution of overloaded functions, producing results that align with the input provided.

A crucial point to note is that the selection of which function to call is determined at compile time, not runtime. The compiler identifies the appropriate function based on the number and types of parameters specified during the function call. The return type does not play a role in this selection process.

The optional keyword overload (used in line 4) is a vestige from C++ version 1.2. In more recent versions, this keyword is not required but can be used for compatibility with older code.

M. Saqib: Saqib is Master-level Senior Software Engineer with over 14 years of experience in designing and developing large-scale software and web applications. He has more than eight years experience of leading software development teams. Saqib provides consultancy to develop software systems and web services for Fortune 500 companies. He has hands-on experience in C/C++ Java, JavaScript, PHP and .NET Technologies. Saqib owns and write contents on mycplus.com since 2004.
Related Post