C++ Notes
main(). The prototype only cares about parameter types, not names: int max(int, int); is valid. The actual definition can appear later, even below main().returnType functionName(parameters) { // code return validValue; }
When you have multiple .cpp files, compilation happens in phases.
| Phase | What Happens |
|---|---|
| Preprocessing | Happens 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. |
| Compilation | Each 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. |
| Linking | The 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.
.h). Headers contain declarations only..cpp). Source files contain definitions.#include "abc.h".cpp that includes them..cpp file is preprocessed and compiled independently.To compile all cpp files together: open a terminal in the project directory and run g++ *.cpp -o Main, then ./Main.
funcDec.h
// declarations only, no definitions here double min(double, double); double max(double, double);
funcDef.cpp
#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
#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; }
| Pass by Value | Pass by Pointer | Pass by Reference | |
|---|---|---|---|
| What's passed | A copy of the value | The address of the variable | The address of the variable |
| Stored in callee as | A brand new variable at a different address | A pointer | A reference, an alias |
| Original modified? | No, changes stay local to the called function | Yes, via dereference * | Yes, no dereference needed |
| Can it be null? | Not applicable | Yes, always check before use | No, must be initialized at declaration |
| Syntax | func(x) / void func(int a) | func(&x) / void func(int* a) | func(x) / void func(int& a) |
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 }
Different signature means different overload. Signature is name plus parameter types, the return type has no say in it.
| Case | Result |
|---|---|
| Different names, same parameters | Valid overload |
| Same name, different parameter types | Valid overload |
| Same name, different parameter order | Valid overload |
| Same name, different number of parameters | Valid overload |
| Same name and parameters, different return type only | Invalid, compilation error |
#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; }
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++.