Modern C++ basics
The compiler pipeline, value and reference semantics, and the modern features that replaced preprocessor habits with type-safe ones.
Compiling C++
C++ is compiled and statically typed like C, but the same source only means one thing once you name a language version. Every command below states it explicitly.
g++ -std=c++20 -Wall -Wextra -Wpedantic -g main.cpp -o app
clang++ -std=c++20 -stdlib=libc++ -Wall -g main.cpp -o app # clang
# several translation units: compile each, then link them together
g++ -std=c++20 -c shapes.cpp -o shapes.o
g++ -std=c++20 -c main.cpp -o main.o
g++ main.o shapes.o -o app| Flag | Purpose |
|---|---|
-std=c++20 | Language version: c++17, c++20, c++23 |
-Wall -Wextra | The warnings worth having on from the first line of code |
-Werror | Refuse to build until the warnings are fixed |
-O2 / -O0 | Release optimisation / debugging |
-g | Debug symbols for gdb and the sanitisers |
-fsanitize=address,undefined | Runtime memory and undefined-behaviour checks |
-Iinclude | Add a directory to the header search path |
-Llib -lmylib | Add a library directory and link a library |
Values, references and const
#include <iostream>
#include <string>
#include <vector>
int main() {
const std::string name = "Ada"; // immutable, and initialisation is required
auto count = 3; // deduced as int
auto ratio = 0.75; // deduced as double
std::vector<int> scores{ 90, 85, 77 };
// pass large objects by const reference: no copy, no accidental modification
auto total = 0;
for (const auto &s : scores) total += s;
// a reference is an alias and cannot be reseated
int a = 1, b = 2;
int &ref = a;
ref = b; // assigns 2 into a; a is still a
std::cout << name << " has " << scores.size() << " scores\n";
return 0;
}autodeduces from the initialiser;auto&keeps a reference, andconst auto&is the usual loop variable.- A pointer may be null and may be reassigned; a reference is always bound to an object and cannot be reseated.
constis a contract that lets both the compiler and the next reader reason about the code, so prefer it by default.- Use
std::stringandstd::vectorinstead of rawchar*andnew[]: they know their own size and release themselves.
Replacements for the old habits
| Old habit | Modern replacement | Why |
|---|---|---|
#define MAX 100 | constexpr int max = 100; | Type-checked, scoped and visible to the debugger |
NULL | nullptr | A real pointer type that cannot silently be the integer 0 |
Manual new and delete | std::unique_ptr | Released exactly once, even when an exception unwinds |
| An index loop over a vector | A range-based for loop | Fewer off-by-one bugs, and the intent is visible |
enum | enum class | Scoped names with no implicit conversion to int |
| Copying arguments | const std::string& | No allocation for read-only access |
constexpr double kPi = 3.14159265358979;
enum class Colour { red, green, blue };
// structured bindings, C++17
auto [lo, hi] = std::pair{ 1, 9 };
// an if with an initialiser keeps a variable's scope tight
std::map<std::string, int> ages{ { "ada", 36 }, { "grace", 45 } };
if (auto it = ages.find("ada"); it != ages.end()) {
std::cout << it->first << " is " << it->second << "\n";
}💡
Turn on
-Wall -Wextra with the first line of code you write, and add -Werror in continuous integration. Retro-fitting warnings onto a finished project is far more painful than learning to read them early.FAQ
Which C++ standard should I target?
C++17 is the safe floor for new code, and C++20 adds concepts, ranges and designated initialisers. Check what your target toolchain actually supports before relying on the newest features.
Is <code>std::vector</code> slower than a C array?
No. Its elements are stored contiguously, exactly like an array, and the size is kept beside the pointer. Any extra indirection usually disappears once the optimiser runs.
Related
Classes, constructors and RAII The standard library and smart pointers
Last refreshed 2026-09-18.