C++ essentials: 50 cards on RAII, smart pointers, move semantics and the STL
Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.
Practice now
Study without an account
Scheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1Reference vs pointerA reference is an alias that must be initialized, can’t be null and can’t be reseated; a pointer holds an address, can be null, can be reassigned, and needs
*to reach the object - 2What does this print?cpp
int a = 1; int& r = a; r = 5; std::cout << a;5:ris another name fora, so assigning through it changesa - 3Three ways to take a
std::stringparameterstd::string scopies it;std::string& slets the function modify the caller’s string;const std::string& sreads it without copyingFor read-only string parameters,std::string_view(C++17) is another option. - 4Why
nullptrinstead ofNULLor0?nullptr(C++11) has its own type,std::nullptr_t, which converts only to pointer types;NULLis usually an integer constant, sof(NULL)can callf(int)instead off(char*) - 5What’s wrong with this function?cpp
const std::string& name() { std::string s = "Ada"; return s; }It returns a reference to a local that is destroyed when the function returns, so using the result is undefined behavior. Returnstd::stringby value insteadReturning by value is cheap thanks to move semantics and copy elision. - 6What is RAII?“Resource acquisition is initialization”: an object acquires a resource in its constructor and releases it in its destructor, so cleanup happens automatically when the object goes out of scope, even if an exception is thrownLocal objects are destroyed at the end of their scope, in reverse order of construction.
- 7Lock a mutex for the rest of the scopecpp
{ std::scoped_lock lock(mtx); shared.push_back(x); } // unlocked here, even if push_back throwsstd::scoped_lockis C++17;std::lock_guard<std::mutex>is the older equivalent. - 8What is
std::unique_ptr<T>?A smart pointer with sole ownership: it deletes the object when it is destroyed. It can be moved but not copied, and typically costs nothing over a raw pointer - 9Create an object owned by a
unique_ptrcppauto w = std::make_unique<Widget>(42);std::make_unique(C++14) avoids a nakednew, so there is never a moment when the object has no owner. - 10Transfer ownership of a
unique_ptrcppauto a = std::make_unique<Widget>(); auto b = std::move(a); // a is now nullauto b = a;doesn’t compile, because aunique_ptrcan’t be copied. - 11What is
std::shared_ptr<T>?A smart pointer with shared ownership: copies share a reference count, and the object is deleted when the lastshared_ptrto it is destroyedCreate it withstd::make_shared<T>(…), which allocates the object and the count together. - 12What is
std::weak_ptrfor?A non-owning reference to an object managed byshared_ptr. It breaks ownership cycles, andlock()returns ashared_ptrif the object still exists (or an empty one if not) - 13Are raw pointers still OK in modern C++?As non-owning observers, yes. Avoid owning raw pointers with manual
newanddelete; let containers and smart pointers own memory - 14What is move semantics?Transferring an object’s resources (like a string’s heap buffer) to another object instead of copying them, when the source is a temporary or no longer neededA move constructor has the signature
T(T&& other) noexcept. - 15What does
std::moveactually do?Nothing at runtime: it is a cast to an rvalue reference, which lets overload resolution pick a move constructor or move assignmentstd::moveon aconstobject silently copies, because the move constructor takes a non-constT&&. - 16What state is an object in after being moved from?Valid but unspecified: you can destroy it or assign it a new value, but shouldn’t rely on its contentsA moved-from
std::stringis usually empty, but the standard doesn’t promise that. - 17lvalue vs rvalueAn lvalue has an identity you can take the address of, like a named variable; an rvalue is a temporary such as
x + 1orstd::string("hi").T&¶meters bind to rvalues - 18Rule of zero vs rule of fiveRule of five: if you define a destructor, copy or move constructor, or copy or move assignment, you probably need all five. Rule of zero (better): use members like
std::vectorandstd::unique_ptrthat manage themselves, and define none - 19Why should move constructors be
noexcept?Whenstd::vectorreallocates, it moves elements only if their move constructor can’t throw; otherwise it copies them (if it can) to keep its strong exception guarantee - 20Which container should you reach for by default?
std::vector: a contiguous, growable array with fast iteration and amortized O(1)push_back - 21What’s wrong here?cpp
std::vector<int> v{1, 2, 3}; int& first = v[0]; v.push_back(4); std::cout << first;Ifpush_backreallocates,firstrefers to freed memory and reading it is undefined behavior. Growing a vector can invalidate all references, pointers and iterators to its elements - 22
std::mapvsstd::unordered_mapstd::mapkeeps keys sorted (a balanced tree, O(log n) operations);std::unordered_mapis a hash table with average O(1) lookups and no ordering - 23What is
m.size()afterwards?cppstd::map<std::string, int> m; if (m["alice"] == 0) { /* … */ }1:operator[]inserts a value-initialized element when the key is missingTo only look, usem.find(key),m.contains(key)(C++20) orm.at(key), which throws if the key is missing. - 24
v[i]vsv.at(i)v[i]doesn’t check bounds, so an out-of-range index is undefined behavior;v.at(i)throwsstd::out_of_range - 25Why use
std::array<int, 3>overint[3]?Same fixed size and no heap allocation, but it knows its size, can be copied and returned by value, works with algorithms, and doesn’t decay to a pointer - 26The
std::string_viewgotchaAstring_view(C++17) doesn’t own its characters. If it outlives the string it points into, such as a temporary, it danglescppstd::string_view sv = std::string("temp"); // dangles immediately - 27
push_backvsemplace_backpush_backtakes an existing object and copies or moves it in;emplace_backforwards constructor arguments and builds the element in placecpppeople.emplace_back("Ada", 36); // calls Person(std::string, int) - 28Sort a vector ascending, then descendingcpp
std::ranges::sort(v); // C++20 std::sort(v.begin(), v.end(), std::greater<>()); // descending - 29Remove all zeros from a vectorcpp
std::erase(v, 0); // C++20 // before C++20: the erase-remove idiom v.erase(std::remove(v.begin(), v.end(), 0), v.end());std::removeonly shifts the kept elements forward and returns the new logical end; it never changes the size. - 30Find a value in a vectorcpp
auto it = std::find(v.begin(), v.end(), 42); if (it != v.end()) { std::cout << "found at " << (it - v.begin()); } - 31Lambda capture lists
[=]captures used variables by copy,[&]by reference,[x, &y]names them individually, and[this]captures the current objectCapturing by reference in a lambda that outlives the scope, such as a stored callback, leaves dangling references. - 32Loop over a
std::vector<std::string>without copying each elementcppfor (const auto& name : names) { std::cout << name << '\n'; }for (auto name : names)copies every string. - 33What’s wrong with this sum?cpp
std::vector<double> prices{1.5, 2.5}; auto total = std::accumulate(prices.begin(), prices.end(), 0);The initial value0is anint, so the sum is accumulated as anint, truncating at each step:totalis3, not4.0. Pass0.0 - 34Read the declaration right to left.
const int* pvsint* const pconst int* p: you can’t modify*p, butpcan point elsewhere.int* const p: you can modify*p, butpcan’t be reseated - 35What does
constafter a member function mean, as inint size() const;?The function promises not to modify the object, and onlyconstmember functions can be called on aconstobject or through aconstreference - 36What does
constexprmean?The value or function can be evaluated at compile time; aconstexprvariable must be initialized with a constant expressioncppconstexpr int square(int x) { return x * x; } std::array<int, square(3)> grid; // size 9, computed at compile time - 37A function templatecpp
template <typename T> T max_of(T a, T b) { return a > b ? a : b; } max_of(3, 7); // T = int max_of(2.5, 1.0); // T = double - 38Why are templates usually defined in headers?The compiler instantiates a template separately for each set of type arguments, and it needs the full definition visible at the point of use to do that
- 39Constrain a template to integer typescpp
template <std::integral T> T gcd(T a, T b) { return b == 0 ? a : gcd(b, a % b); }Concepts (C++20) give clear errors instead of a page of template failures;std::integralis in<concepts>. - 40Does this copy the string?cpp
std::vector<std::string> names = load(); auto first = names[0];Yes:autodrops references (and top-levelconst), sofirstis a copy. Writeauto& firstorconst auto& firstto refer to the element - 41What is undefined behavior?Code the standard places no requirements on. The compiler may assume it never happens, so the program might crash, give wrong results, or seem to work until it doesn’t
- 42Signed vs unsigned integer overflowSigned overflow (
INT_MAX + 1) is undefined behavior; unsigned arithmetic wraps around modulo 2ⁿOptimizers exploit this:x + 1 > xmay be folded totruefor a signedx. - 43What does this print?cpp
int x; std::cout << x;Anything: reading an uninitialized local variable is undefined behavior. Always initialize:int x = 0;orint x{};C++26 reclassifies it as “erroneous behavior”, which is still a bug. - 44Common sources of undefined behaviorOut-of-bounds access, dereferencing null or dangling pointers, use after free, double
delete, signed overflow, reading uninitialized variables, and data racesSanitizers catch many of them: compile with-fsanitize=address,undefined. - 45What is a data race?Two threads access the same non-atomic variable without synchronization, and at least one of them writes. It is undefined behavior; use
std::mutexorstd::atomic - 46When does a base class need a virtual destructor?When objects may be deleted through a pointer to the base; without it,
delete base_ptron a derived object is undefined behaviorRule of thumb: a class with any virtual function should have a virtual destructor. - 47What does
Widget w();declare?A function namedwthat returns aWidget, not an object (the “most vexing parse”). WriteWidget w;orWidget w{}; - 48
int x = 3.7;vsint x{3.7};The first silently truncates to3; the second doesn’t compile, because brace initialization rejects narrowing conversions - 49Return “a value or nothing” from a functioncpp
std::optional<User> find_user(int id); if (auto user = find_user(42)) { std::cout << user->name; }std::optionalis C++17;value_or(fallback)supplies a default. - 50What does
overridedo?Asks the compiler to verify that the function overrides a base-class virtual function, turning a signature mismatch into a compile errorcppvoid draw() const override;
Frequently asked questions
Which C++ standard does the deck use?
Modern C++, mostly C++17 and C++20 idioms. Newer features are marked with their standard, such as std::make_unique (C++14), std::optional and string_view (C++17), and concepts and ranges (C++20).
Is this deck for C++ beginners?
It suits anyone who knows basic C++ syntax, or comes from C or another language, and wants the modern idioms: ownership, move semantics, the STL and the pitfalls behind undefined behavior.
Why so many cards on undefined behavior?
Because UB is where C++ differs most from safer languages: buggy code often compiles and appears to work. Knowing the common sources is the best defense, together with sanitizers.
More in Programming
JavaScript
Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.
50 cardsAdvanced JS
Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.
50 cardsTypeScript
Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.
50 cardsPython
Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.
50 cardsGit & terminal
The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.
50 cardsJava
Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.
50 cardsC#
C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.
50 cardsKotlin
Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.
50 cardsSwift
Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.
50 cardsFlutter
Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.
50 cardsPHP
Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.
50 cardsRuby
Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.
50 cardsGo
Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.
50 cardsRust
Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.
50 cardsHaskell
Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.
50 cardsElixir
Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.
50 cardsx86-64 assembly
x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.
50 cardsRegex
Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.
50 cards