Introduction & Environment Setup¶
Welcome to the Modern C++ Masterclass! C++ is one of the most widely used systems programming languages, providing both low-level hardware control and high-level abstract features. This course is specifically tailored for development on the Raspberry Pi, showing you how to build fast, robust, and modern applications.
1. What is C++? A Brief History¶
C++ was created by Bjarne Stroustrup at Bell Labs starting in 1979 as an extension to the C language. Originally called "C with Classes," it was renamed C++ in 1983 (where ++ is the increment operator, symbolizing progress).
Over the decades, C++ has evolved through several standardizations managed by the ISO committee:
| Era | Standard | Key Paradigm / Main Focus |
|---|---|---|
| Classic | C++98 / C++03 | Object-Oriented Programming (OOP) and introduction of the STL (Standard Template Library). |
| Modern C++ | C++11 | A major reboot. Introduced move semantics, auto, lambdas, smart pointers, and multi-threading. |
| C++14 | Bug fixes and minor updates to C++11 features (e.g., generic lambdas). | |
| C++17 | Structured bindings, inline variables, std::optional, std::variant, and filesystem API. |
|
| Modern Evolution | C++20 | Concepts, Ranges, Coroutines, Modules, and std::format. |
| C++23 | std::print/std::println, std::expected, std::mdspan, and compiler improvements. |
On platforms like the Raspberry Pi, modern C++ allows you to write high-level hardware control logic without paying a performance penalty. The core philosophy of C++ is "Zero-Overhead Abstraction" — you don't pay for what you don't use.
2. Setting up the C++ Environment on Raspberry Pi¶
To write modern C++, we need an up-to-date compiler toolchain. Raspberry Pi OS (Bookworm and later) is based on Debian, shipping with recent versions of GCC (GNU Compiler Collection).
Installing Build Tools¶
Open your terminal and run the following command to install the essential packages:
build-essential: Installsg++(C++ compiler),gcc(C compiler),make(build tool), and fundamental system libraries.cmake: A meta-build system generator used in almost all professional C++ projects.gdb: The GNU Debugger, used for diagnosing segfaults and debugging runtime execution.
Verifying GCC Compiler Support¶
Confirm the version of g++ installed by running:
Ideally, you want GCC 12 or later (shipped with Raspberry Pi OS Bookworm) to ensure solid support for C++20 and initial support for C++23.
3. The C++ Compilation Pipeline¶
Unlike languages like Python, C++ code is not interpreted line-by-line at runtime. Instead, it must be compiled into machine-readable binary code before execution. The compiler goes through four primary stages:
Let's examine how you can manually inspect each phase of this process.
Step 1: Preprocessing¶
The preprocessor resolves preprocessor directives starting with #. It copies headers into the source file and handles conditional compilation macros (e.g. #ifdef).
To inspect preprocessor output, use the -E flag:
main.i in a text editor to see how a simple file grows to thousands of lines after copying <iostream>).
Step 2: Compilation¶
The compiler parses the C++ code, optimizes it, and translates it into assembly language.
To stop after the compilation phase and output assembly instructions, use the -S flag:
Step 3: Assembly¶
The assembler takes assembly text files and translates them into binary machine instructions, creating an object file (e.g., .o or .obj).
To compile to an object file without linking, use the -c flag:
Step 4: Linking¶
The linker merges your object files with compiled code from runtime libraries (like libc++ or libstdc++) to resolve addresses and create the final executable.
4. Analyzing Your First C++ Program¶
Let's analyze the components of a simple C++ program.
Deep Dive into Code Semantics¶
#include <iostream>: Tells the preprocessor to load the stream Input/Output header. It contains declarations forstd::cout,std::cin, andstd::cerr.int main(): Every runnable C++ application requires a global function namedmain. It must return anint.std::cout: Stands for "Standard Character Output". Thestd::prefix indicates it belongs to thestd(standard) namespace. Namespaces prevent name collisions in large projects.<<: The stream insertion operator. It pushes the string on the right side into the stream on the left side (std::cout).std::endl: A stream manipulator. It inserts a newline (\n) and flushes the output buffer (forcing immediate terminal output). For performance-critical code, using'\n'is preferred overstd::endlto avoid redundant buffer flushing.return 0;: In C++, returning0from themainfunction signals to the operating system that the program terminated successfully. Non-zero values indicate errors.
5. Compiler Flags and Optimization¶
When compiling code, passing appropriate flags to the compiler dictates performance, code safety, and diagnostics.
-std=c++20: Explicitly instructs the compiler to build with C++20 features enabled. For C++23, use-std=c++23.-Wall: Enables "all" common warning messages. High-quality code should compile with zero warnings.-Wextra: Enables additional warning messages not covered by-Wall. Helpful for catching subtle logic errors.-O2: Turns on compiler optimizations. The compiler optimizes CPU registers and instruction orders, making the program run significantly faster. For maximum optimizations, use-O3. During debugging, use-O0(no optimization) or-Og(debug-friendly optimization) along with the-gflag to generate debugging symbols.
6. Project Automation with CMake¶
As soon as a project has more than one source file, running g++ manually becomes tedious. CMake is the industry standard tool to generate system-native build files (like Makefiles).
1. Create a Project Directory¶
2. Create the Source File¶
Save the "Hello, World" code into src/main.cpp.
3. Create the CMakeLists.txt¶
In the root directory of my_project/, create a file named CMakeLists.txt:
4. Build and Run the App¶
Always use a separate build directory to prevent generated files from cluttering your source code:
7. Basic Interactive Input/Output¶
Besides writing to standard output, a command-line program often requires reading user input. C++ provides std::cin for input streams.
std::cin >> age: Extracts data from standard input into the variable. It stops reading at whitespace.std::getline(std::cin, full_name): Used to read a full line of text including spaces. Sincestd::cin >>leaves a newline character (\n) in the stream, we callstd::cin.ignore()first to flush any remaining character in the buffer before reading the string.
Related Guides¶
- Basic Syntax — Dig into variables, types, and modern C++ assignments.
- Build Systems — Mastering advanced CMake layouts on Raspberry Pi.