← Back to all posts

11 C++ Notes

Functions, Part 1: Basics and Overloading

C++ Notes


Introduction

What Is a Function

C++
returnType functionName(parameters) {
    // code
    return validValue;
}

Compilation Model

When you have multiple .cpp files, compilation happens in phases.

PhaseWhat Happens
PreprocessingHappens before compilation on each file. #include directives are resolved by literally pasting the header file content in, macros (#define) are expanded, and comments are removed.
CompilationEach preprocessed .cpp file becomes a Translation Unit (TU) and is compiled independently into an object file (.o or .obj), which is machine code plus unresolved symbols, references to things defined elsewhere.
LinkingThe linker combines all object files, resolves cross-TU references, and produces the final executable.

If something is declared but never defined anywhere, you get a linker error, not a compiler error, since the compiler was satisfied that a definition would exist somewhere, it's the linker that discovers it never showed up.

Multiple CPP Files

To compile all cpp files together: open a terminal in the project directory and run g++ *.cpp -o Main, then ./Main.


Code Example: Three Files

funcDec.h

C++
// declarations only, no definitions here
double min(double, double);
double max(double, double);

funcDef.cpp

C++
#include "funcDec.h"  // include header so compiler can check against declarations

double min(double a, double b) {
    return (a < b) ? a : b;
}

double max(double a, double b) {
    return (a > b) ? a : b;
}

main.cpp

C++
#include <iostream>
#include "funcDec.h"

int main() {
    double x = 10.5;
    double y = 20.3;
    std::cout << "Min: " << min(x, y) << std::endl; // prints: 10.5
    std::cout << "Max: " << max(x, y) << std::endl; // prints: 20.3
    return 0;
}

Passing Arguments

Three Ways to Pass Arguments

Pass by ValuePass by PointerPass by Reference
What's passedA copy of the valueThe address of the variableThe address of the variable
Stored in callee asA brand new variable at a different addressA pointerA reference, an alias
Original modified?No, changes stay local to the called functionYes, via dereference *Yes, no dereference needed
Can it be null?Not applicableYes, always check before useNo, must be initialized at declaration
Syntaxfunc(x) / void func(int a)func(&x) / void func(int* a)func(x) / void func(int& a)

Copy Elision (RVO / NRVO)

C++
string make() {
    string a{"hello"};
    string b{"world"};
    string c = a + b;
    return c; // c may be built directly inside x (NRVO)
}
int main() {
    string x = make(); // memory for x reserved first, then function runs
}

Function Overloading

Different signature means different overload. Signature is name plus parameter types, the return type has no say in it.

CaseResult
Different names, same parametersValid overload
Same name, different parameter typesValid overload
Same name, different parameter orderValid overload
Same name, different number of parametersValid overload
Same name and parameters, different return type onlyInvalid, compilation error

Code Example: main.cpp

C++
#include <iostream>
using namespace std;

// 1) pass by value: copy is modified, original untouched
void func1(int a) {
    a *= 100;
    cout << "Inside func1 (copy): " << a << endl; // prints: 200
}

// 2) pass by pointer: original modified via dereference
void func2(int* a) {
    *a *= 100;
    cout << "Inside func2 (ptr):  " << *a << endl; // prints: 300
}

// 3) pass by reference: original modified via alias
void func3(int& a) {
    a *= 100;
    cout << "Inside func3 (ref):  " << a << endl; // prints: 400
}

int main() {
    int x1{2};
    int x2{3};
    int x3{4};

    cout << "Before: x1=" << x1 << " x2=" << x2 << " x3=" << x3 << endl;

    func1(x1);   // copy: x1 unchanged after call
    func2(&x2);  // pointer: x2 is changed
    func3(x3);   // reference: x3 is changed

    cout << "After:  x1=" << x1 << " x2=" << x2 << " x3=" << x3 << endl;
    // prints: x1=2 (unchanged), x2=300 (changed), x3=400 (changed)

    return 0;
}

Closing

Functions, why the linker exists, and the three ways data can cross a function boundary. Next up, part 2: lambdas, templates, and C++20 concepts, the more modern half of what a "function" can mean in C++.

← Previous 10 - Char Arrays and String Manipulation Next → 12 - Functions, Part 2: Lambdas, Templates, and Concepts