Master these 31 carefully curated interview questions to ace your next Cpp interview.
C++ is an object-oriented extension of C adding classes, templates, STL, RAII, exceptions, and strong type safety.
C++ adds: classes/objects, inheritance, polymorphism, templates (generics), STL containers/algorithms, RAII for resource management, exceptions, namespaces, references, operator overloading. Backward compatible with most C code. Modern C++ (11/14/17/20/23) has smart pointers, lambdas, ranges, concepts, modules.
RAII ties resource lifetime to object lifetime — resources acquired in constructor, released in destructor automatically.
Example: std::unique_ptr automatically frees memory when scope ends. File handles in std::fstream close on destruction. Benefits: exception-safe, no resource leaks, deterministic cleanup. Foundation of modern C++ — prefer RAII over manual new/delete.
Smart pointers (unique_ptr, shared_ptr, weak_ptr) manage dynamic memory automatically, preventing leaks.
unique_ptr: exclusive ownership, non-copyable, zero overhead. shared_ptr: reference-counted shared ownership. weak_ptr: non-owning observer of shared_ptr (breaks cycles). Use make_unique/make_shared. Prefer unique_ptr by default. Never use raw new/delete in modern C++.
Templates enable generic programming — write code once that works with any type, resolved at compile time.
Function templates: template<typename T> T max(T a, T b). Class templates: template<typename T> class Stack. Template specialization for specific types. Variadic templates for variable arguments. SFINAE and concepts (C++20) for constraints. STL is built entirely on templates. Compile-time polymorphism.
Standard Template Library provides containers (vector, map), algorithms (sort, find), and iterators for generic programming.
Containers: sequential (vector, deque, list, array), associative (map, set, multimap), unordered (unordered_map, unordered_set). Algorithms: sort, find, transform, accumulate, for_each. Iterators: connect containers and algorithms. C++20 ranges: lazy, composable alternative to iterator pairs.
Move semantics transfer resources from temporary objects instead of copying, using rvalue references (&&) for performance.
std::move casts to rvalue reference, enabling move constructor/assignment. Move constructor: steals resources, leaves source in valid but empty state. Rule of Five: if you define destructor/copy constructor/copy assignment, also define move constructor/move assignment. Eliminates expensive deep copies for temporaries.
Concepts constrain template parameters with readable requirements, replacing SFINAE with clear, expressive constraints.
template<typename T> concept Sortable = requires(T t) { std::sort(t.begin(), t.end()); }. Usage: template<Sortable T> void mysort(T& container). Benefits: better error messages, readable constraints, documentation. Standard concepts: std::integral, std::floating_point, std::same_as, std::ranges::range.
Profile with perf/Valgrind/VTune, identify hotspots, optimize algorithms, reduce copies, use move semantics and caching.
Steps: (1) Profile: perf record/report, Valgrind --tool=callgrind, Intel VTune. (2) Optimize algorithms first (O(n²) → O(n log n)). (3) Reduce copies: pass by reference, use move semantics. (4) Cache-friendly data (SoA vs AoS). (5) Avoid virtual function calls in hot paths. (6) Use constexpr for compile-time computation. (7) Parallel algorithms (std::execution::par).
If you define any of: destructor, copy constructor, copy assignment, move constructor, or move assignment — define all five.
Managing resources (raw pointers, file handles) requires all five for correct behavior. Rule of Zero: prefer no custom resource management — use smart pointers and RAII containers. If a class owns a resource, implement all five. = default and = delete help express intent clearly.
RAII (Resource Acquisition Is Initialization) ties resource lifetime to object lifetime — resources are released in destructors.
Principle: acquire resources in constructors, release in destructors. The compiler guarantees destructor calls, even during exceptions. Examples: std::unique_ptr (memory), std::lock_guard (mutex), std::fstream (files), std::vector (dynamic array). Benefits: no manual cleanup, exception-safe, no resource leaks. Anti-pattern: new/delete manually (use smart pointers). RAII is the foundation of C++ resource management — replaces try-finally in other languages. Rule of Five: if custom destructor needed, also define copy/move constructors and assignments.
Smart pointers (unique_ptr, shared_ptr, weak_ptr) automatically manage memory lifetime, preventing leaks and dangling pointers.
unique_ptr: sole ownership, moved (not copied), zero overhead. std::make_unique<T>(args). shared_ptr: shared ownership via reference counting, overhead for counter. std::make_shared<T>(args). weak_ptr: non-owning reference to shared_ptr, prevents circular references, lock() to get shared_ptr. Rules: (1) Default to unique_ptr. (2) shared_ptr only when truly shared ownership. (3) never use raw new/delete. (4) weak_ptr breaks cycles (observer pattern). Custom deleters for non-memory resources (files, sockets).
Value copies the data; reference is an alias to original; pointer stores the address. References cannot be null or reassigned.
By value: function gets copy, original unchanged. Pass large objects by const reference. By reference (int& x): alias, no copy, modifies original. Cannot be null, cannot be reassigned. By pointer (int* x): stores address, can be null, can be reassigned, need dereference (*x). C++ guidelines: const reference for read-only large objects, reference for output parameters, pointer only when null is valid. Move semantics (T&&): transfers ownership without copy — enables efficient return of local objects.
Virtual functions enable runtime polymorphism — base class pointer calls derived class implementation through the vtable mechanism.
virtual keyword in base class: virtual void draw(). Override in derived. Called via base pointer/reference: Base* b = new Derived(); b->draw() calls Derived::draw(). Vtable: compiler creates table of function pointers per class. override keyword (C++11): catches mismatches. pure virtual (= 0): makes class abstract. Virtual destructor: essential when deleting derived through base pointer. CRTP (Curiously Recurring Template Pattern): static polymorphism without vtable overhead. Cost: vtable pointer per object (~8 bytes), indirect function call.
Move semantics transfer resources from temporary objects instead of copying, using rvalue references (T&&) for efficiency.
Problem: copying large objects is expensive. Solution: 'steal' resources from temporaries. Rvalue reference (T&&): binds to temporaries and std::move'd objects. Move constructor: MyClass(MyClass&& other) noexcept — takes ownership of other's resources, leaves other in valid but empty state. std::move: casts lvalue to rvalue reference (doesn't actually move). Return value optimization (RVO): compiler eliminates copies. Rule of Five: if custom destructor, define copy constructor, copy assignment, move constructor, move assignment. noexcept on moves enables std::vector optimizations.
Templates enable generic programming by parameterizing functions and classes with types, resolved at compile time.
Function template: template <typename T> T max(T a, T b). Class template: template <typename T> class Stack { T data[]; }. Specialization: template<> class Stack<bool> — optimized for specific type. Partial specialization: for class templates only. SFINAE (Substitution Failure Is Not An Error): enables template metaprogramming. Concepts (C++20): template <std::integral T> T add(T a, T b) — constrained templates with clear error messages. Variadic templates: template <typename... Args> — variable argument count. Templates are instantiated at compile time — code bloat is possible.
STL provides generic containers (vector, map, set), algorithms (sort, find), and iterators for efficient, reusable data operations.
Containers: vector (dynamic array), deque (double-ended), list (doubly linked), map/set (red-black tree), unordered_map/set (hash table), array (fixed), stack/queue (adaptors). Algorithms: sort, find, binary_search, transform, accumulate, remove_if, min/max_element. Iterators: bridge between containers and algorithms. Iterator categories: input, output, forward, bidirectional, random access. Ranges (C++20): views and pipe syntax, lazy evaluation. Choose: vector by default, unordered_map for O(1) lookups, set for ordered unique elements.
C++ memory model defines thread interaction guarantees. Atomics provide lock-free operations with configurable memory ordering.
Memory orders: memory_order_relaxed (no ordering), memory_order_acquire (read barrier), memory_order_release (write barrier), memory_order_seq_cst (default, strongest). std::atomic<T>: atomic operations without locks. load(), store(), exchange(), compare_exchange_strong/weak. Use cases: lock-free counters, flags, lock-free data structures. std::atomic_flag: guaranteed lock-free boolean. Fences: std::atomic_thread_fence as standalone barriers. Performance: relaxed is fastest but hardest to reason about. Default seq_cst is safest. Lock-free programming is expert-level — prefer mutexes unless performance demands it.
Concepts constrain template parameters with readable requirements, providing clear error messages instead of cryptic template errors.
Syntax: template <typename T> concept Sortable = requires(T a) { { a < a } -> std::convertible_to<bool>; }. Usage: template <Sortable T> void sort(T& container). Standard concepts: std::integral, std::floating_point, std::same_as, std::derived_from, std::invocable. requires clause: template<typename T> requires Comparable<T>. Advantages over SFINAE: readable error messages, composable (Concept1 && Concept2), self-documenting. Compile-time checked. Overload resolution: more constrained template wins. Replaces enable_if and tag dispatch patterns.
Compile-time uses templates/CRTP (zero overhead, resolved at compile); runtime uses virtual functions (vtable, resolved at execution).
Runtime (dynamic): virtual functions, vtable lookup, base class pointers. Cost: vtable pointer per object, indirect function call. Flexibility: add derived classes without recompiling. Compile-time (static): templates, CRTP (Curiously Recurring Template Pattern), concepts. CRTP: class Derived : Base<Derived> — base calls derived without virtual. Zero overhead: resolved at compile time, fully inlineable. Trade-off: compile-time polymorphism has no virtual overhead but increases compile time and binary size, cannot store heterogeneous collections easily.
Throw by value, catch by const reference, use RAII for cleanup, mark noexcept when possible, and avoid in destructors.
Best practices: (1) Throw: throw std::runtime_error('message'). (2) Catch: catch (const std::exception& e) by const reference. (3) RAII ensures cleanup even during exceptions — no try/catch for cleanup. (4) noexcept: enables optimizations, documents intent. (5) Never throw in destructors — std::terminate called. (6) Custom exceptions: derive from std::exception. (7) std::current_exception/rethrow for exception transport between threads. (8) Exception safety guarantees: basic (no leak), strong (rollback), nothrow. Alternatives: std::expected (C++23), error codes for performance-critical paths.
Profile first, use appropriate data structures, minimize allocations, leverage move semantics, and enable compiler optimizations.
Steps: (1) Profile: perf, Vtune, Google Benchmark. (2) Algorithm complexity: O(n log n) vs O(n²). (3) Data structures: vector > list (cache-friendly), flat_map for small maps. (4) Minimize copies: move semantics, return by value (RVO), pass by const reference. (5) Avoid allocations: reserve() vector capacity, stack allocation, custom allocators. (6) Cache-friendly: SoA pattern, sequential access. (7) Compiler: -O2/-O3, LTO (link-time optimization), PGO (profile-guided). (8) SIMD: std::execution::par, intrinsics. (9) constexpr: compute at compile time.
Rule of Zero: use automatic resource management. Rule of Five: if custom destructor, define all 5 special members. Rule of Three was pre-C++11.
Rule of Zero: prefer using standard library types (smart pointers, containers) so you don't need custom destructors, copy/move constructors, or assignments. Rule of Five (C++11): if you define any of: destructor, copy constructor, copy assignment, move constructor, move assignment — define ALL five. Rule of Three (C++03): destructor + copy constructor + copy assignment (no move). = default: use compiler-generated. = delete: prevent operation (e.g., non-copyable). Recommendation: Rule of Zero > Rule of Five. Only manage resources directly when wrapping C APIs or implementing containers.
variant: type-safe union (one of N types); optional: value or nothing; any: type-erased container for any single value.
std::optional<T> (C++17): represents value or none. optional<int> result. has_value(), value(), value_or(default). Use instead of sentinel values (-1) or output parameters. std::variant<Types...> (C++17): type-safe tagged union. variant<int, string> v. std::visit for pattern matching. Replaces union with type safety. std::any (C++17): holds any type, runtime type checking. any_cast<T> throws on wrong type. Use sparingly — prefer variant. Together: optional for 'might not exist', variant for 'one of these types', any for truly dynamic types.
Coroutines are functions that can suspend and resume execution, enabling async I/O, generators, and lazy evaluation.
Keywords: co_await (suspend until ready), co_yield (produce value and suspend), co_return (final value). Components: promise_type (customization point), coroutine_handle (control resume/destroy), awaiter (awaitable objects). Generator example: generator<int> range(int n) { for (int i=0; i<n; i++) co_yield i; }. Async: co_await async_read() suspends until I/O completes. Stackless: state stored on heap. Libraries: cppcoro, libcoro simplify usage. Compiler generates state machine. Applications: async I/O, network programming, game logic, data streaming.
Lambdas are anonymous function objects with capture syntax, enabling inline callbacks, algorithms, and closures.
Syntax: [capture](params) -> return_type { body }. Captures: [x] by value, [&x] by reference, [=] all by value, [&] all by reference, [this] class members. Generic lambdas (C++14): [](auto x) { return x*2; }. constexpr lambdas (C++17). Template lambdas (C++20): []<typename T>(T x) {}. Immediately invoked: [](){ init(); }(). Use with algorithms: std::sort(v.begin(), v.end(), [](auto a, auto b) { return a > b; }). std::function<void()> can store lambdas (type erasure, heap allocation). Prefer auto for lambda storage.
new/delete call constructors/destructors and are type-safe; malloc/free only allocate/free raw memory without initialization.
new: allocates memory + calls constructor, returns typed pointer, throws std::bad_alloc on failure. delete: calls destructor + frees memory. malloc: allocates raw bytes, returns void*, returns NULL on failure, no constructor. free: frees memory, no destructor. new[]/delete[]: array versions. Placement new: construct in existing memory: new (buffer) Object(). Use smart pointers (make_unique, make_shared) instead of raw new. Never mix (new with free, or malloc with delete). C++ memory: stack, heap, static, thread-local.
Use smart pointers (unique_ptr/shared_ptr), RAII pattern, avoid raw new/delete, and use tools like Valgrind and ASAN.
Prevention: (1) smart pointers: unique_ptr for sole ownership, shared_ptr for shared. (2) RAII: resources tied to object lifetime. (3) Container usage: vector/string manage their memory. (4) make_unique/make_shared: exception-safe allocation. (5) Never use raw new/delete. (6) Rule of Zero: let standard types handle resources. Detection: Valgrind, AddressSanitizer (-fsanitize=address), LeakSanitizer, Visual Studio diagnostics. Static analysis: clang-tidy, cppcheck. Common leaks: exception between new and delete (solved by smart pointers), circular shared_ptr (use weak_ptr), forgotten delete in error paths.
std::move casts to rvalue reference enabling move semantics; perfect forwarding preserves value category through template functions.
std::move: static_cast to rvalue reference — signals that object can be moved from. Does NOT actually move anything. After move, object is in valid but unspecified state. Perfect forwarding: template<typename T> void wrapper(T&& arg) { func(std::forward<T>(arg)); }. Universal reference (T&&): deduced as lvalue ref or rvalue ref depending on argument. std::forward: preserves original value category (lvalue stays lvalue, rvalue stays rvalue). Used in: factory functions, emplace operations, make_unique/make_shared. Reference collapsing rules: T& && → T&, T&& && → T&&.
SFINAE (Substitution Failure Is Not An Error) enables conditional template instantiation based on type properties at compile time.
When template argument substitution fails, that specialization is silently removed from overload set instead of causing error. Classic: template<typename T> typename std::enable_if<std::is_integral<T>::value, T>::type process(T val). C++17: if constexpr is often simpler. C++20: Concepts replace most SFINAE uses. std::void_t: utility for SFINAE detection. decltype in trailing return type: auto func(T t) -> decltype(t.method()). Use cases: enable functions only for certain types, detect member functions/operators, provide different implementations per type. Concepts are the modern replacement — clearer, shorter, better errors.
C++11+ provides threads, mutexes, condition variables, futures, and atomics in the standard library for concurrent programming.
std::thread: create threads. std::mutex + std::lock_guard/unique_lock: mutual exclusion. std::condition_variable: thread signaling. std::future/std::promise/std::async: async results. std::atomic: lock-free operations. std::shared_mutex (C++17): reader-writer locks. std::jthread (C++20): auto-joining, cooperative cancellation. Thread pool: not in standard — use BS::thread_pool or custom. Data races: undefined behavior — use atomics or mutexes. Parallel algorithms (C++17): std::sort(std::execution::par, ...). Performance: minimize locking, prefer lock-free when possible, avoid false sharing.
Ready to master Cpp?
Start learning with our comprehensive course and practice these questions.