Skip to content

C++

C++ Logo

C++ started as "C with Classes" in 1979 and grew into a massive language. It's C's powerful sibling you get C's low level control plus OOP, templates, exceptions, and a huge standard library.

Table of Contents

Part 1: Getting Started

1. Why C++ (and why modern-C++)

C++ runs the world's most performance critical software. Web browsers (Chrome, Firefox), game engines (Unreal, Unity), databases (MySQL, MongoDB), and operating system components all rely on C++ for one reason: when you need speed and control, C++ delivers.

The Evolution:

C++98 was... functional. It worked. But it was verbose, error prone, and had too many ways to shoot yourself in the foot. Modern C++ (C++11 and beyond) is a different language: - Smart pointers eliminate manual memory management - RAII ensures resources are cleaned up automatically - Auto and type deduction reduce verbosity - Ranges make algorithms readable - Concepts provide better error messages - Strong standard library means less custom code

Modern C++ Philosophy:

The old C++ way: "Trust me, I know what I'm doing." The modern way: "Let the language help me avoid mistakes."

Principles for Safe C++: - Prefer values, references, and STL containers over raw pointers - Use smart pointers (std::unique_ptr, std::shared_ptr) instead of new/delete - Avoid raw memory manipulation unless absolutely necessary (and document why) - Enable compiler warnings (-Wall -Wextra -Wpedantic), use sanitizers, and run static analyzers - Write code that's hard to use incorrectly (make interfaces safe by default)

2. What you need to install

Pick a compiler and basic tools:

  • Linux/macOS: Install GCC or Clang Debian/Ubuntu: sudo apt get update && sudo apt get install -y build essential clang Fedora: sudo dnf install gcc c++clangmacOS:xcode select --install(then install Homebrew and optionallybrew install llvm`)
  • Windows: Install Visual Studio (Desktop development with C++) or MSYS2/MinGW (pacman -S mingw w64-x86_64-gcc)
  • CMake (portable build system): https://cmake.org/download/
  • A good editor: VS Code, CLion, Visual Studio, Qt Creator, Vim/Neovim, Emacs

3. Your first program

hello.cpp:

#include <iostream>

int main() {
    // This is your entry point
    std::cout << "Hello, C++!" << '\n'; // Print a friendly greeting
}
}

Compile and run (Linux/macOS with g++):

# Compile
g++ -std=c++20 -O2 -Wall -Wextra hello.cpp -o hello

# Run
./hello

That Compiler Flag Breakdown: - -std=c++20: Use C++20 standard (or c++17, c++11 depending on what you need) - -O2: Optimization level 2 (faster code, but slower compilation) - -Wall -Wextra: Enable warnings (catch mistakes early)

Windows (MSVC Developer Command Prompt):

cl /std:c++20 /W4 /EHsc hello.cpp
hello.exe

4. Compile and run (CLI +-CMake)

Common compiler flags:

  • Language standard: -std=c++17 or -std=c++20
  • Warnings: -Wall -Wextra -Wpedantic (GCC/Clang) or /W4 /permissive- (MSVC)
  • Debug: -g (GCC/Clang) or /Zi (MSVC)
  • Optimization: -O2/-O3 (GCC/Clang) or /O2 (MSVC)
  • Sanitizers (Clang/GCC): -fsanitize=address,undefined,thread

Minimal CMake project:

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)
project(hello LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(hello src/main.cpp)

# Recommended warnings on GCC/Clang
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
  target_compile_options(hello PRIVATE -Wall -Wextra -Wpedantic -Wconversion -Wsign conversion)
endif()

# Recommended warnings on MSVC
if (MSVC)
  target_compile_options(hello PRIVATE /W4 /permissive-)
endif()

src/main.cpp

#include <iostream>
int main() {
    std::cout << "Hello from CMake!
";
}

Build:

mkdir build && cd build
cmake .. && cmake --build .

5. Project layout

A simple, scalable layout:

project/
  CMakeLists.txt
  cmake/
  include/
    project/
      api.hpp
  src/
    main.cpp
    api.cpp
  tests/
    CMakeLists.txt
    test_api.cpp
  third_party/

Part 2: C++ Language Fundamentals

6. Comments and structure

  • Single line comments use //
  • Multi line comments use /* ... */
  • Your program starts at int main() { ... }
#include <iostream>

int main() {
    // Print to stdout
    std::cout << "Line 1" << '\n';
    std::cout << "Line 2" << std::endl; // std::endl flushes the output buffer
}

Style note: Avoid using namespace std;

This guide intentionally avoids writing using namespace std; at global scope and instead qualifies standard library names with std::.

Reasons: - Namespace pollution: Pulls hundreds of names into the global namespace, increasing chances of collisions with your own symbols (e.g., size, distance, begin/end, swap) and platform macros (e.g., Windows min/max). - Header hygiene: If placed in a header, it leaks into every translation unit that includes it, causing surprising errors elsewhere. - Overload/ADL pitfalls: Unqualified calls can bind to unintended overloads via argument dependent lookup, leading to subtle bugs. - Clarity and maintainability: std:: makes ownership explicit and improves searchability and code reviews. - Future proofing: As the standard adds more names, full qualification reduces ambiguity.

Prefer these alternatives when brevity helps: - Narrow using declarations in small scopes (function/block), not in headers: using std::string; using std::cout; - Namespace aliases for long names: namespace fs = std::filesystem; - Purpose specific literal namespaces: using namespace std::chrono_literals; or using namespace std::string_literals; in limited scope.

Bad (global directive):

using namespace std; // Avoid
vector<int> v; string s; cout << v.size() << ' ' << s.size() << '\n';

Better (qualified or narrow scope):

#include <vector>
#include <string>
#include <iostream>

int main() {
    std::vector<int> v;
    std::string s;
    std::cout << v.size() << ' ' << s.size() << '\n';
}

#include <iostream>
#include <string>
using std::cout;    // Narrow scope and specific names
using std::string;  // Avoid in headers; fine in small .cpp scope

int main() {
    string s = "hi";
    cout << s.size() << '\n';
}

Note: In this guide, using namespace std::chrono_literals; appears in a small scope to enable time literals like 100ms; this is considered safe and intentional.

7. Types and variables

Here are the fundamental types you'll use:

  • bool, char, wchar_t, char8_t, char16_t, char32_t
  • Signed/unsigned integers: short, int, long, long long
  • Floating point: float, double, long double
  • size_t (unsigned size/index), ptrdiff_t (signed pointer difference)
#include <cstdint>

int main() {
    bool ok = true;           // true or false
    char c = 'A';             // single character
    std::uint32_t u = 42;     // 32-bit unsigned integer
    std::int64_t big = -1;    // 64-bit signed integer
    double pi = 3.14159;      // double precision float
    auto inferred = 10;       // compiler infers int
}

Use fixed width types from for clarity (e.g., std::int32_t, std::uint64_t).

7b. Data types: sizes, ranges, buffers, and special cases

  • The standard only guarantees minimum ranges and relative relationships. Always use sizeof(T) and std::numeric_limits for portable code.
  • Common 64-bit ABIs and what they imply for sizes: LP64 (Linux/macOS, GCC/Clang): int=32, long=64, pointer=64 LLP64 (Windows/MSVC): int=32, long=32, long long=64, pointer=64
  • Typical sizes (do not hardcode; measure with sizeof): bool: 1 byte (value is 0 or 1 when converted to int) char: 1 byte; signedness is implementation defined (use signed char/unsigned char or std::int8_t/std::uint8_t) wchar_t: 2 bytes on Windows (UTF-16 code unit), 4 bytes on Linux (UTF-32 code unit) char8_t (C++20): 1 byte UTF-8 code unit; char16_t: 2 bytes; char32_t: 4 bytes short: ≥16-bit (commonly 2 bytes) int: ≥16-bit (commonly 4 bytes) long: LP64=8 bytes, LLP64=4 bytes long long: ≥64-bit (commonly 8 bytes) float: 4 bytes double: 8 bytes; long double: 16 bytes on GCC/Clang x86-64 Linux (80-bit extended precision storage), often 8 bytes on MSVC/macOS size_t, ptrdiff_t: same width as pointer (32-bit on 32-bit, 64-bit on 64-bit) Pointers: typically 8 bytes on 64-bit, 4 bytes on 32-bit. Do not assume function and object pointers have the same representation.
  • Enums: Unscoped enum underlying type is implementation defined (usually int). Prefer enum class and specify underlying type if size matters (e.g., enum class Color : std::uint8_t {...}).
  • Alignment and padding: Struct members can be padded; sizeof(struct) ≥ sum of member sizes. Use alignof(T) to inspect alignment. Avoid packed layouts unless interfacing with wire formats.
  • Endianness: Varies by platform (x86/x86-64 is little endian). For I/O and protocols use explicit byte order conversions; do not reinterpret multi byte values across machines without conversion.

Buffers, arrays, and strings

  • C-style strings (char[]): must include space for the NUL terminator '\0'. Example: char buf[6] = "hello"; // 5 chars + 1 terminator
  • std::string: Contiguous storage. data() and c_str() point to a NUL-terminated buffer; size() excludes the terminator. capacity() may exceed size(). Growth can reallocate and invalidate pointers/iterators. Small string optimization (SSO) is implementation detail; never rely on a minimum in place capacity.
  • std::string_view: Non owning view; not guaranteed NUL-terminated; lifetime must outlive the view; do not store views to temporaries.
  • std::array: Fixed size buffer; sizeof(std::array) == N * sizeof(T) (no heap); contiguous; bounds unchecked with operator[].
  • std::vector: Dynamic, contiguous buffer; size() vs capacity(); push_back/emplace_back may reallocate. Use reserve() to pre allocate; shrink_to_fit() is non binding.
  • std::span (C++20): Non owning view into a contiguous buffer; stores pointer and length (in elements). Does not own memory.
  • std::byte (C++17): Prefer for raw byte buffers instead of unsigned char when semantics are bytes, not characters.

Printing sizes and detecting ABI at runtime

#include <iostream>
#include <cstdint>
#include <limits>

int main() {
    std::cout << "sizeof(void*)=" << sizeof(void*) << "\n";
    std::cout << "sizeof(long)=" << sizeof(long) << ", sizeof(long long)=" << sizeof(long long) << "\n";
    std::cout << "char is signed? " << std::boolalpha << std::numeric_limits<char>::is_signed << "\n";
    bool lp64  = sizeof(long) == 8 && sizeof(void*) == 8;
    bool llp64 = sizeof(long) == 4 && sizeof(void*) == 8;
    std::cout << "LP64=" << lp64 << ", LLP64=" << llp64 << "\n";
}

Guidance

  • Prefer fixed width types from when size matters (e.g., std::uint32_t).
  • For binary I/O, use reinterpret_cast(buf) only at the I/O boundary and ensure sizes are in bytes: count * sizeof(T).
  • Avoid assuming wchar_t is Unicode scalar value width; prefer char8_t/char16_t/char32_t for explicit encodings and libraries for conversions.

8. Operators

  • Arithmetic: + * / %
  • Comparison: == != < > <= >=
  • Logical: && || !
  • Bitwise: & | ^ ~ << >>
  • Assignment: = += -= *= /= %= &= |= ^= <<= >>=
  • Increment/decrement: ++ --

9. Control flow

#include <iostream>

int main() {
    int x = 7;

    if (x > 5) {
        std::cout << ">5\n";
    } else if (x == 5) {
        std::cout << "=5\n";
    } else {
        std::cout << "<5\n";
    }

    switch (x) {
        case 1: std::cout << "one\n"; break;
        case 7: std::cout << "seven\n"; break;
        default: std::cout << "other\n"; break;
    }

    for (int i = 0; i < 3; ++i) {
        std::cout << i << ' ';
    }
    std::cout << '\n';

    int y = 0;
    while (y < 3) {
        std::cout << y++ << ' ';
    }
    std::cout << '\n';
}

10. Functions

  • For big objects, pass by const& to avoid copies.
  • Returning values is cheap thanks to Return Value Optimization (RVO).
#include <string>
#include <iostream>

std::string greet(const std::string& name) {
    return "Hello, " + name + "!";
}

int add(int a, int b) { return a + b; }

int main() {
    std::cout << greet("World") << '\n';
    std::cout << add(2, 3) << '\n';
}

Overloading and default parameters:

int area(int w, int h) { return w * h; }
int area(int side) { return side * side; } // overload
int volume(int l, int w, int h = 1) { return l * w * h; } // default

11. References vs pointers

  • Reference (&): must bind to an object, cannot be null, usually safer.
  • Pointer (*): can be null, can be reseated, must be managed carefully.

Pointer concept

#include <iostream>

void inc_ref(int& v) { v += 1; }
void inc_ptr(int* v) { if (v) *v += 1; }

int main() {
    int x = 10;
    inc_ref(x);
    inc_ptr(&x);
    std::cout << x << '
'; // 12
}

12. Arrays and strings

Prefer std::array (fixed size) and std::vector (dynamic) over raw arrays. Prefer std::string.

#include <array>
#include <vector>
#include <string>
#include <iostream>

int main() {
    std::array<int, 3> a = {1, 2, 3};
    std::vector<int> v = {1, 2, 3, 4};
    v.push_back(5);

    std::string s = "secure";
    s += " coding";

    std::cout << a[0] << ' ' << v.back() << ' ' << s << '
';
}

Part 3: The Standard Library-(STL)

13. Containers

  • Sequence: std::vector, std::array, std::deque, std::list, std::forward_list
  • Associative: std::set, std::multiset, std::map, std::multimap
  • Unordered (hash based): std::unordered_set, std::unordered_map
  • Adapters: std::stack, std::queue, std::priority_queue

Use cases:

  • Default to std::vector for dynamic arrays
  • Use std::map/std::unordered_map for key/value
  • Use std::string for text; std::string_view for non owning string slices
#include <vector>
#include <map>
#include <unordered_map>
#include <string>
#include <iostream>

int main() {
    std::vector<int> nums = {1,2,3};
    nums.insert(nums.begin(), 0);

    std::map<std::string, int> ordered{{"alice",1},{"bob",2}};
    std::unordered_map<std::string, int> fast{{"alice",1},{"bob",2}};

    std::cout << ordered["alice"] << ' ' << fast["bob"] << '
';
}

14. Algorithms

Prefer algorithms over manual loops: std::sort, std::transform, std::accumulate, std::find_if, std::any_of, std::all_of, std::remove_if, etc.

#include <algorithm>
#include <numeric>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {4,1,3,2};
    std::sort(v.begin(), v.end());

    int sum = std::accumulate(v.begin(), v.end(), 0);
    bool any_even = std::any_of(v.begin(), v.end(), [](int x){ return x % 2 == 0; });

    v.erase(std::remove_if(v.begin(), v.end(), [](int x){ return x < 3; }), v.end());

    std::cout << sum << ' ' << any_even << ' ' << v.size() << '
';
}

15. Iterators and Ranges

Ranges (C++20) make code concise and safe.

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {1,2,3,4,5};
    auto squares = v | std::views::transform([](int x){ return x*x; })
                      | std::views::filter([](int x){ return x % 2 == 0; });
    for (int x : squares) std::cout << x << ' '; // 4 16
    std::cout << '
';
}

16. Time and chrono

#include <chrono>
#include <thread>
#include <iostream>

int main() {
    using namespace std::chrono_literals; // enables 100ms, 2s literals
    auto start = std::chrono::steady_clock::now();
    std::this_thread::sleep_for(100ms);
    auto end = std::chrono::steady_clock::now();
    std::cout << "Elapsed: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(end start).count()
              << " ms
";
}

17. Filesystem

#include <filesystem>
#include <iostream>

int main() {
    namespace fs = std::filesystem;
    for (auto const& entry : fs::directory_iterator{"."}) {
        std::cout << entry.path() << '
';
    }
}

18. Formatting and I/O

Prefer std::format (C++20/23) for safe, readable formatting.

#include <format>
#include <iostream>

int main() {
    std::string s = std::format("User: {} Score: {}", "alice", 42);
    std::cout << s << '
';
}

Fallback for older compilers: {fmt} library (https://github.com/fmtlib/fmt).

19. Optional, Variant, Any, Expected

  • std::optional: maybe a value
  • std::variant: tagged union
  • std::any: type erased container (avoid unless needed)
  • std::expected (C++23 or via tl::expected): return value or error
#include <optional>
#include <string>
#include <iostream>

std::optional<int> to_int(const std::string& s) {
    try {
        size_t idx = 0;
        int v = std::stoi(s, &idx);
        if (idx != s.size()) return std::nullopt;
        return v;
    } catch (...) {
        return std::nullopt;
    }
}

int main() {
    if (auto v = to_int("123")) std::cout << *v << '
';
}

20. Random

Use for reproducible PRNG (not cryptographically secure).

#include <random>
#include <iostream>

int main() {
    std::mt19937 rng{std::random_device{}()};
    std::uniform_int_distribution<int> dist(1, 6);
    std::cout << dist(rng) << '
';
}

For cryptography, use dedicated libraries; do not use std::random for secrets.

21. Regex

#include <regex>
#include <string>
#include <iostream>

int main() {
    std::regex re{"^([a z]+)\d{2}$"};
    std::string s = "user42";
    std::cout << std::boolalpha << std::regex_match(s, re) << '
';
}

Part 4: Object Oriented Programming-(OOP)

22. Classes and structs

#include <string>
#include <iostream>

class User {
public:
    User(std::string name, int score) : name_(std::move(name)), score_(score) {}

    void add_score(int delta) { score_ += delta; }
    int score() const { return score_; }
    const std::string& name() const { return name_; }

private:
    std::string name_;
    int score_;
};

int main() {
    User u{"alice", 10};
    u.add_score(5);
    std::cout << u.name() << ": " << u.score() << '
';
}
  • struct defaults to public members; class defaults to private

23. Constructors, destructors, copy/move

#include <vector>
#include <iostream>

struct Buffer {
    std::vector<int> data;

    Buffer() = default;
    explicit Buffer(size_t n) : data(n) {}

    // Rule of 0: let the compiler generate special members when using RAII types
};

int main() {
    Buffer a{10};
    Buffer b = a;        // copy
    Buffer c = std::move(a); // move
    std::cout << b.data.size() << ' ' << c.data.size() << '
';
}

If you manage raw resources, define copy/move explicitly or disable them. Prefer RAII types to avoid manual management.

24. Inheritance and polymorphism

#include <iostream>
#include <memory>

struct Shape {
    virtual ~Shape() = default;
    virtual double area() const = 0; // pure virtual
};

struct Rect : Shape {
    double w{}, h{};
    Rect(double w, double h) : w(w), h(h) {}
    double area() const override { return w * h; }
};

int main() {
    std::unique_ptr<Shape> s = std::make_unique<Rect>(2.0, 3.0);
    std::cout << s->area() << '
';
}

Use override, final, and virtual destructors for polymorphic bases.

25. Rule of 0/3/5 and RAII

  • Rule of 0: if all members are RAII types, no need to define destructor/copy/move.
  • Rule of 3: if you define any of destructor, copy ctor, copy assign, define all three.
  • Rule of 5: include move ctor and move assign in modern C++.
  • RAII: acquire resources in constructor, release in destructor.

Program memory layout

Part 5: Modern C++ Features

26. Smart pointers

Prefer smart pointers over raw new/delete.

  • std::unique_ptr: exclusive ownership
  • std::shared_ptr: shared ownership
  • std::weak_ptr: non owning reference to shared object
#include <memory>
#include <string>

struct Thing { std::string name; };

std::unique_ptr<Thing> make_unique_thing() {
    return std::make_unique<Thing>(Thing{"alpha"});
}

int main() {
    auto t = make_unique_thing();
    // transfer ownership
    auto t2 = std::move(t);
}

Avoid shared_ptr unless sharing is necessary (it has overhead). Avoid cycles with shared_ptr; break them with weak_ptr.

27. Auto, range for, structured bindings

#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string,int> m{{"a",1},{"b",2}};

    for (const auto& [k,v] : m) {
        std::cout << k << ": " << v << '
';
    }
}

28. Lambdas

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v{1,2,3,4};
    int threshold = 2;
    auto cnt = std::count_if(v.begin(), v.end(), [threshold](int x){ return x > threshold; });
    std::cout << cnt << '
';
}

29. Templates and Concepts

#include <concepts>
#include <iostream>

template <std::integral T>
T add(T a, T b) { return a + b; }

int main() {
    std::cout << add(3, 4) << '
';
}

Concepts provide clear constraints and better error messages.

30. Modules-(C++20+)

Modules speed up builds and improve encapsulation. Toolchain support varies.

// math.ixx (interface unit)
export module math;
export int add(int a, int b);
// math.cpp
module math;
export int add(int a, int b) { return a + b; }
// main.cpp
import math;
#include <iostream>
int main(){ std::cout << add(2,3) << '
'; }

31. Coroutines-(C++20+)

Coroutines enable async style code. Libraries provide helpers (cppcoro, etc.). Minimal example outlines the idea; production requires promise types/awaitables.

// Pseudocode level sketch, not production ready
// co_return, co_await, and co_yield appear in coroutine functions

32. Ranges-(C++20+)

Already shown above; prefer range pipelines for clarity.

Part 6: Concurrency and Parallelism

33. Threads and mutexes

#include <thread>
#include <mutex>
#include <vector>
#include <iostream>

int main() {
    std::mutex m;
    int counter = 0;
    auto work = [&](){
        for (int i = 0; i < 1000; ++i) {
            std::lock_guard<std::mutex> lock(m);
            ++counter;
        }
    };

    std::thread t1(work), t2(work);
    t1.join(); t2.join();
    std::cout << counter << '
';
}

34. Atomics

#include <atomic>
#include <thread>
#include <vector>
#include <iostream>

int main() {
    std::atomic<int> counter{0};
    auto work = [&](){ for (int i = 0; i < 100000; ++i) counter.fetch_add(1, std::memory_order_relaxed); };
    std::thread t1(work), t2(work);
    t1.join(); t2.join();
    std::cout << counter.load() << '
';
}

35. Async and futures

#include <future>
#include <thread>
#include <iostream>

int compute() { std::this_thread::sleep_for(std::chrono::milliseconds(100)); return 42; }

int main() {
    auto fut = std::async(std::launch::async, compute);
    std::cout << fut.get() << '
';
}

36. Parallel algorithms

#include <algorithm>
#include <execution>
#include <vector>

int main() {
    std::vector<int> v(1'000'000, 1);
    std::for_each(std::execution::par, v.begin(), v.end(), [](int& x){ x += 1; });
}

Part 7: I/O and Data Handling

37. I/O, files, and serialization

38. Text and binary files

#include <fstream>
#include <vector>
#include <string>

int main() {
    // Write text
    {
        std::ofstream out{"out.txt"};
        out << "hello
";
    }

    // Read text
    {
        std::ifstream in{"out.txt"};
        std::string line;
        while (std::getline(in, line)) {
            // process line
        }
    }

    // Write binary
    {
        std::vector<int> data{1,2,3,4};
        std::ofstream out{"data.bin", std::ios::binary};
        out.write(reinterpret_cast<const char*>(data.data()), data.size()*sizeof(int));
    }

    // Read binary
    {
        std::ifstream in{"data.bin", std::ios::binary};
        std::vector<int> buf(4);
        in.read(reinterpret_cast<char*>(buf.data()), buf.size()*sizeof(int));
    }
}

39. JSON (3rd-party)

Use nlohmann/json (header only) or simdjson for performance.

// Example with nlohmann/json (https://github.com/nlohmann/json)
#include <nlohmann/json.hpp>
#include <string>
#include <iostream>

int main() {
    nlohmann::json j;
    j["user"] = "alice";
    j["score"] = 42;
    std::cout << j.dump(2) << '
';
}

Part 8: Secure Coding Practices

40. Secure C++: Do this, avoid that

This section prioritizes safe practices. When in doubt, prefer higher level abstractions and library facilities.

41. Avoid dangerous functions

  • Do not use: gets, strcpy, strcat, sprintf, vsprintf, scanf without width limits, strtol without checks, strtok, asctime, ctime (unsafe variants)
  • Prefer: std::string, std::string_view, std::snprintf, std::getline, iostreams, std::from_chars/to_chars
#include <charconv>
#include <string>
#include <system_error>
#include <iostream>

std::optional<int> parse_int(std::string_view sv) {
    int value{};
    auto* first = sv.data();
    auto* last  = sv.data() + sv.size();
    auto [ptr, ec] = std::from_chars(first, last, value);
    if (ec == std::errc{} && ptr == last) return value;
    return std::nullopt;
}

int main() {
    if (auto v = parse_int("123")) std::cout << *v << '
';
}

42. Bounds and input validation

  • Always check sizes and bounds.
  • Use at() for bounds checked access in safety critical code.
  • Validate untrusted input rigorously.
#include <vector>
#include <iostream>

void safe_access(const std::vector<int>& v, size_t i) {
    if (i < v.size()) {
        std::cout << v[i] << '
';
    } else {
        std::cout << "index out of range
";
    }
}

43. Integer safety

  • Watch for signed/unsigned conversions and overflow.
  • Prefer fixed width types and explicit casts only when necessary.
  • Use checked math helpers (e.g., Abseil, SafeInt) in critical code.
#include <cstdint>
#include <limits>
#include <iostream>

bool add_will_overflow(std::uint32_t a, std::uint32_t b) {
    return b > std::numeric_limits<std::uint32_t>::max() a;
}

44. Lifetime and ownership

  • Prefer values and std::unique_ptr for ownership.
  • Avoid raw new/delete; avoid manual arrays; use std::vector/std::array.
  • Avoid dangling references by ensuring the owner outlives the reference.
#include <memory>
#include <string>

std::unique_ptr<std::string> make_name() {
    return std::make_unique<std::string>("alice");
}

45. Undefined behavior and portability

  • Do not dereference null or invalid pointers.
  • Do not access out of bounds.
  • Do not violate strict aliasing.
  • Keep to the standard; be careful with compiler specific extensions.

46. Filesystem safety

  • Normalize and validate paths; avoid directory traversal.
  • Avoid following untrusted symlinks; use std::filesystem::is_symlink and canonical.
  • Restrict file permissions when creating files.
#include <filesystem>
#include <iostream>

bool safe_subpath(const std::filesystem::path& base, const std::filesystem::path& user) {
    auto canon_base = std::filesystem::weakly_canonical(base);
    auto canon_user = std::filesystem::weakly_canonical(canon_base / user);
    return std::ranges::equal(canon_base, canon_user.begin(), canon_base.end());
}

47. Cryptography guidance

  • Do not implement cryptographic primitives yourself.
  • Use vetted libraries (libsodium, Botan, OpenSSL via safe wrappers).
  • Use proper randomness for keys (OS-provided CSPRNG). Avoid std::random for secrets.
  • Ensure constant time comparisons for secrets where applicable.

48. Concurrency pitfalls

  • Data races cause undefined behavior.
  • Protect shared data with mutexes or atomics.
  • Prefer immutable data and message passing when possible.

49. Sanitizers and hardening

  • AddressSanitizer (ASan): detects out of bounds and use after free
  • UndefinedBehaviorSanitizer (UBSan): catches undefined behavior
  • ThreadSanitizer (TSan): detects data races
  • MemorySanitizer (MSan): uninitialized memory reads

GCC/Clang example (debug build):

  • -fsanitize=address,undefined -fno omit frame pointer -g

Binary hardening (Linux):

  • -D_FORTIFY_SOURCE=2 -fstack protector strong -fPIE -pie -Wl,-z,relro,-z,now

50. Static analysis and formatting

  • clang tidy: style and bug finding
  • cppcheck: static analysis
  • include what you use: header hygiene
  • clang format: consistent formatting

Part 9: Build, Test, and Deploy

51. Build systems and toolchains

52. Compilers-(GCC/Clang/MSVC)

  • Prefer latest stable versions for better diagnostics and features.
  • Enable high warning levels and treat warnings as errors in CI.

GCC/Clang flags:

  • -Wall -Wextra -Wpedantic -Wconversion -Wsign conversion -Wshadow -Wnon virtual dtor

MSVC flags:

  • /W4 /permissive- /EHsc

53. CMake basics

Modern usage: target based commands.

cmake_minimum_required(VERSION 3.23)
project(secproj LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(secproj_lib src/api.cpp)
target_include_directories(secproj_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)

add_executable(secproj_app src/main.cpp)
target_link_libraries(secproj_app PRIVATE secproj_lib)

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
  target_compile_options(secproj_app PRIVATE -Wall -Wextra -Wpedantic)
endif()

54. Unit testing-(GoogleTest)

# tests/CMakeLists.txt
add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/googletest googletest build)

add_executable(tests test_api.cpp)
target_link_libraries(tests PRIVATE gtest gtest_main secproj_lib)
add_test(NAME all_tests COMMAND tests)
// tests/test_api.cpp
#include <gtest/gtest.h>
TEST(Sample, Basic) { EXPECT_EQ(2+2, 4); }

Part 10: Advanced Topics & Patterns

55. Design patterns and idioms

56. Pimpl

Hide implementation details to reduce compile time coupling.

// widget.hpp
#include <memory>
class widget {
public:
    widget();
    ~widget();
    widget(widget&&) noexcept;
    widget& operator=(widget&&) noexcept;
    void do_work();
private:
    struct impl;
    std::unique_ptr<impl> pimpl;
};
// widget.cpp
#include "widget.hpp"
#include <iostream>
struct widget::impl { void do_work_impl(){ std::cout << "work
"; } };
widget::widget() : pimpl(std::make_unique<impl>()) {}
widget::~widget() = default;
widget::widget(widget&&) noexcept = default;
widget& widget::operator=(widget&&) noexcept = default;
void widget::do_work(){ pimpl->do_work_impl(); }

57. Type erasure

#include <memory>
#include <utility>
#include <iostream>

class any_drawable {
    struct concept_t {
        virtual ~concept_t() = default;
        virtual void draw() const = 0;
    };
    template <class T>
    struct model_t : concept_t {
        T obj;
        explicit model_t(T o) : obj(std::move(o)) {}
        void draw() const override { obj.draw(); }
    };
    std::unique_ptr<concept_t> self;
public:
    template <class T>
    any_drawable(T obj) : self(std::make_unique<model_t<T>>(std::move(obj))) {}
    void draw() const { self->draw(); }
};

struct square { void draw() const { std::cout << "[]
"; } };

int main(){ any_drawable d = square{}; d.draw(); }

58. CRTP

#include <iostream>

template <class Derived>
struct base {
    void interface(){ static_cast<Derived*>(this)->impl(); }
};

struct derived : base<derived> {
    void impl(){ std::cout << "CRTP
"; }
};

int main(){ derived d; d.interface(); }

59. Visitor

#include <variant>
#include <iostream>

using value = std::variant<int, double, const char*>;

struct visitor_t {
    void operator()(int v) const { std::cout << v << '
'; }
    void operator()(double v) const { std::cout << v << '
'; }
    void operator()(const char* v) const { std::cout << v << '
'; }
};

int main(){ value v = 3.14; std::visit(visitor_t{}, v); }

Part 11: Help and Resources

60. Troubleshooting and FAQ

  • You want help ? remember the golden advice GOOGLE IS YOUR FRIEND
  • C++ reference: https://en.cppreference.com/
  • Core Guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
  • CMake: https://cmake.org/cmake/help/latest/
  • Sanitizers: https://github.com/google/sanitizers
  • fmt: https://github.com/fmtlib/fmt
  • GoogleTest: https://github.com/google/googletest
  • Ranges: https://en.cppreference.com/w/cpp/ranges
  • Coroutines TS: https://en.cppreference.com/w/cpp/language/coroutines
  • Filesystem: https://en.cppreference.com/w/cpp/filesystem

Sources

  • C++ reference (cppreference): https://en.cppreference.com/
  • ISO C++ (WG21) papers and drafts: https://wg21.link/
  • C++ Core Guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
  • isocpp.org (news, FAQs): https://isocpp.org/
  • GCC C++ options: https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html
  • Clang driver and diagnostics: https://clang.llvm.org/docs/UsersManual.html
  • MSVC C++ docs: https://learn.microsoft.com/cpp/
  • CMake documentation: https://cmake.org/cmake/help/latest/
  • GoogleTest documentation: https://google.github.io/googletest/
  • {fmt} library: https://fmt.dev/latest/index.html
  • Ranges (C++20): https://en.cppreference.com/w/cpp/ranges
  • Coroutines: https://en.cppreference.com/w/cpp/language/coroutines
  • Filesystem: https://en.cppreference.com/w/cpp/filesystem
  • and PRNGs: https://en.cppreference.com/w/cpp/numeric/random
  • std::format (C++20/23): https://en.cppreference.com/w/cpp/utility/format
  • Sanitizers (ASan/UBSan/TSan/MSan): https://github.com/google/sanitizers
  • Abseil (checked integer helpers, base): https://abseil.io/
  • Conan/vcpkg package managers: https://conan.io/ | https://vcpkg.io/