Learning materials

User-defined Functions

On this page

User-defined functions are the fundamental unit to organize code in C++ programming language.

Purpose

  • Readability, maintainability
    • Shorter function body
    • Unique name for a code block
  • Code reuse, write once, use many times
  • Affect the outside world
    • Return a value
    • Modify reference or pointer-typed parameters
    • Input/output
    • Modify global variables (AVOID!)

Declaration

  • Optional but strongly recommended
  • Declarations in header file enables function reuse in another cpp file
  • May omit parameter names
  • May provide default value for parameters
  • Public function declarations
    • Functions to be used by other cpp files
    • In the header files
    • Add documentation
  • Private function declarations
    • Functions to be used only in the current cpp file only
    • In the cpp files
  • Return type
    • a concrete type: expecting a value of the specified type
    • void: not expecting anything

Definition (implementation)

  • In the cpp files
  • Formal parameters
    • Parameters listed in the parenthesis
    • Must have types and names specified
    • May provide default value for parameters
    • Stored the same way as local variables
  • Must align to declarations
  • Return statement
    • may have multiple in a same function definition
    • returns a value as a copy (by-value) to the caller (except in void functions)

Default value

  • Provide starting from last to the first parameter
  • Must only appear once, either in declaration or definition
  • Default value in declarations is recommended

Function call

  • Functions must be either declared or defined before being called
  • Actual parameters
    • Values provided in function calls
    • Rvalues in the parenthesis
    • Never add types
  • Usages
    • as a statement strcpy(str1, str2);
    • as part of an expression (non-void functions) cout << sqrt(4) << endl;

Function Pitfalls

  • Declaration/definition after call
  • Default values both in declaration and definition of a same function
  • No parameter name in definition
  • Type of parameter provided in function call
// ==== functions ====

// ---- bad default values ----

// v1 cannot have default value if v2 do not have
void func1(int v1 = 10, int v2);

// both have default value
void func1(int v1 = 10);
void funct(int v1 = 10) { ... }

// ---- wrong definitions ----
void func1(int) { ... }

// ---- wrong function calls ----
func1(int a);

Parameter Passing

  • parameter passing is the same as initialization (not assignment!)
    • type conversion will happen as needed
  • Pass by value
    • The default method to pass parameters
    • The value of the actual parameter is used to initialize the formal parameter
    • A copy is created like in a normal initialization
  • Pass by pointer
    • A memory address is passed by value
  • Passing arrays
  • Pass by reference

Scope

  • Definition: The life span of an entity (variable, function, type, etc.)

  • local scope to a function: local variables and formal parameters

    • NEVER return a local variable by reference or pointer!
    • NEVER return a local array (only pointer will be passed out, the array content will not)! Return a dynamic array instead.
  • Code block based scope

    // example 1, out-of-scope access
    // for loop is a block even without {}
    for (int i = 0; i < size; ++i)
      cout << i << endl;
    cout << i << endl;  // will fail because i is not declared here
    
    // example 2, shadowed name
    int i = 100;
    {
      int i = 10;  // treated as another variable, valid in this block only
      cout << i << endl;  // get 10, outside i is shadowed
    }
    cout << i << endl;  // get 100
    
    // returning an dynamic array
    // should be deleted after use
    int *makeArray(int size) {
      return new int[size];
    }
    
    // ---- Wrong ----
    // returning local array
    int *makeArray(int size) {
      int array[size];
      return array;
    }
    
    // ---- Wrong ----
    // returning local variable by reference/pointer
    string *func() {
      string str = "abc";
      return &str;  // memory address is passed out, str will be destroyed
    }
    

Overloading

Same name but different parameters (type, order, number) so the function call can match the correct function. Because the return type and formal parameter names does not affect the function call, differences in return type and formal parameter names will not be valid overloading.

// ==== Function Overloading ====

// given this function
int func1(int name);  // func1(3);

// ---- Correct ----
int func1(double);  // func1(3.0);
int func1(int, int);  // func1(3, 3);
int func1(int, double);  // func1(3, 3,0);
int func1(double, int);  // func1(3.0, 3);
int func1(double, double);  // func1(3.0, 3.0);

// ---- Wrong ----
double func1(int);  // return type differs, also matches func1(3); call
int func1(int name1);  // parameter names differ, also matches func1(3); call

Function call matching

Function name overloading, default value, and implicit type conversion will all affect function call matching process. Any ambiguous function call will cause an syntax error.

  • call matching mechanism
    • return type does not matter
    • default value should be considered
    • Exact match: no implicit type conversion
    • Compatible match: need implicit type conversion
    • order of matching
      1. Exact matching
      2. Compatible matching
  • ambiguous matching
    • multiple exact matches
    • multiple compatible matches
    • an exact match will not conflict with compatible matches
// Wrong
// both exact matches
// func1(1); call is ambiguous with
void func1(int);
void func1(int, int = 1);

// Wrong
// both compatible matches
// func2(2, 3); call is ambiguous with
void func2(int, double);
void func2(double, int);

// Correct
// first is an exact match
// second is a compatible match
// func2(2, 3.0); call is fine with
void func2(int, double);
void func2(double, double);

Example

#include <iostream>

using namespace std;

/**
  * function to double a value
  * myParam: the input value to be doubled
  * return: a double value
  * documentations should be above the declaration
  */
// int doubleValue(int = 10);  // default value, no name
int doubleValue(int myParam = 10);  // default value, with name

int doubleValue(int param1){ // parameter name can even be different
  return param1 * 2;
}

int localFunc() {
  cout << "This is local" << endl;
  return 0;
}

int main() {
  // function calls
  cout << "double_value() gives " << doubleValue() << endl;  // get 20
  cout << "double_value(5) gives " << doubleValue(5) << endl; // get 10

  return EXIT_SUCCESS;
}

Function Glossary

Function name
The name to a function given by the user. Same naming convention as variables. You cannot use a same name for a function and a variable either.
Function Parentheses
A pair of parentheses must be present to differentiate a function name from a variable name.
Function declaration
A specification of the return type, function name and parameter types. How a function can be called and what you can expect from the call.
Function definition
The implementation of the function. How is the work done in a function.
Function call
The syntaxes to invoke the function by providing actual parameters and optionally receive the returned value
Formal parameters
Sometimes referred as parameters. They are the names listed in the parentheses in the function definition. Each name should have a type specified. Formal parameters are stored in the same place as the local variables in the stack frame of the function call.
Actual parameters
The rvalues provided in the parenthesis in a function call.
Parameter Passing
A mechanism to initialize the value of formal parameters using the values of actual parameters provided in the function call.
Return statement
A statement employed in a function to exit the function and return a value (except in a void function).
Function overloading
The mechanism that allows multiple declarations using a same function name but different parameter list. Also applies to methods.
Function Call Matching
A mechanism to identify the right function definition to execute given a function call.
Scope
The lifespan of names of entities in C++(variables, functions, types, etc.) The C++ language has a block-based scope.