Toolmingo
Guides23 min read

50 C++ Interview Questions (With Answers)

Top C++ interview questions with clear answers and code examples — covering memory management, OOP, templates, STL, concurrency, move semantics, and modern C++17/20.

C++ interviews test memory management, object-oriented design, template metaprogramming, the STL, concurrency, and modern C++11–20 features. This guide covers the 50 most common questions — with clear answers and runnable examples.

Quick reference

Topic Most asked questions
Memory Stack vs heap, new/delete, smart pointers, RAII
OOP Virtual functions, vtable, abstract classes, multiple inheritance
Templates Function templates, class templates, specialization, SFINAE
STL vector vs list, map vs unordered_map, iterators, algorithms
Move semantics lvalue vs rvalue, move constructor, perfect forwarding
Concurrency threads, mutex, atomic, condition_variable
Modern C++ auto, lambda, constexpr, structured bindings, ranges (C++20)

Memory management

1. What is the difference between stack and heap memory?

Stack memory is automatically managed — allocated when a function is called and released when it returns. It is fast but limited in size. Heap memory is manually managed (or via smart pointers) and lasts until explicitly freed.

void example() {
    int x = 42;          // stack — freed when function returns
    int* p = new int(42); // heap — must call delete p;
    delete p;
}

Stack overflow occurs when recursion or large local arrays exceed the stack limit. Heap fragmentation and leaks occur when new is not paired with delete.

2. What are smart pointers? When do you use each?

Smart pointers manage heap memory automatically via RAII. The three main types:

Smart pointer Ownership Use case
unique_ptr<T> Exclusive Single owner; move-only
shared_ptr<T> Shared (ref-counted) Multiple owners
weak_ptr<T> Non-owning observer Break shared_ptr cycles
#include <memory>

// unique_ptr — sole owner
auto u = std::make_unique<int>(42);

// shared_ptr — shared ownership
auto s1 = std::make_shared<int>(42);
auto s2 = s1;  // ref count → 2

// weak_ptr — observe without ownership
std::weak_ptr<int> w = s1;
if (auto locked = w.lock()) {
    std::cout << *locked << "\n";
}

Rule: prefer unique_ptr by default; use shared_ptr only when shared ownership is truly needed; use weak_ptr to break cycles (e.g., parent–child back-pointers).

3. What is RAII?

RAII (Resource Acquisition Is Initialization) is the C++ idiom of tying resource lifetime to object lifetime. The resource is acquired in the constructor and released in the destructor, so it is automatically freed when the object goes out of scope — even if an exception is thrown.

class FileHandle {
    FILE* f_;
public:
    explicit FileHandle(const char* path) : f_(fopen(path, "r")) {
        if (!f_) throw std::runtime_error("cannot open file");
    }
    ~FileHandle() { fclose(f_); }  // always called
    FILE* get() const { return f_; }
};

Smart pointers, std::lock_guard, std::fstream, and std::vector all follow RAII.

4. What is the difference between new/delete and malloc/free?

Feature new/delete malloc/free
Calls constructors/destructors Yes No
Returns typed pointer Yes void*
On failure throws std::bad_alloc returns nullptr
Array form new[]/delete[] malloc(n*sizeof(T))
C++ idiomatic Yes No (C style)

Mixing new with free or malloc with delete is undefined behavior.

5. What is a memory leak? How do you detect one?

A memory leak occurs when heap memory is allocated but never freed, causing the process to consume ever-increasing memory. Common causes: forgetting delete, early returns before delete, exceptions thrown before cleanup.

Detection tools:

  • Valgrind (valgrind --leak-check=full ./app)
  • AddressSanitizer (-fsanitize=address)
  • Visual Studio Diagnostics Tools (Windows)
  • Using smart pointers prevents most leaks by design.

Object-oriented programming

6. What is the difference between struct and class in C++?

The only difference is the default access specifier:

struct class
Default member access public private
Default inheritance public private
Conventional use Plain data (POD) Full OOP types
struct Point { int x, y; };          // members public by default
class Circle { int radius_; };        // radius_ private by default

7. What is a virtual function? How does the vtable work?

A virtual function enables runtime polymorphism — the correct overriding function is called based on the actual (dynamic) type of the object, not the static type.

class Animal {
public:
    virtual void speak() const { std::cout << "...\n"; }
    virtual ~Animal() = default;   // always virtual destructor in base
};

class Dog : public Animal {
public:
    void speak() const override { std::cout << "Woof\n"; }
};

Animal* a = new Dog();
a->speak();  // prints "Woof" — dynamic dispatch
delete a;

vtable: Each class with virtual functions gets a static table of function pointers (vtable). Each object holds a hidden vptr pointing to the class's vtable. A virtual call dereferences vptr to find the correct function — one indirection level compared to a non-virtual call.

8. What is a pure virtual function and an abstract class?

A pure virtual function (= 0) has no default implementation in the base class. A class with at least one pure virtual function is abstract and cannot be instantiated.

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

class Circle : public Shape {
    double r_;
public:
    explicit Circle(double r) : r_(r) {}
    double area() const override { return 3.14159 * r_ * r_; }
};

// Shape s;   // error: cannot instantiate abstract class
Shape* s = new Circle(5.0);

9. What is multiple inheritance and what is the diamond problem?

C++ allows a class to inherit from multiple base classes. The diamond problem occurs when two base classes share a common ancestor, and the derived class gets two copies of the grandparent.

struct A { int x; };
struct B : A {};
struct C : A {};
struct D : B, C {};  // D has two copies of A::x — ambiguous

// Fix: virtual inheritance
struct B2 : virtual A {};
struct C2 : virtual A {};
struct D2 : B2, C2 {};  // only one shared copy of A::x

10. What is the Rule of Three / Five / Zero?

Rule When to apply What to define
Rule of Three (C++03) Class manages a resource Copy constructor, copy assignment, destructor
Rule of Five (C++11) Class manages a resource Above + move constructor, move assignment
Rule of Zero No raw resource management Use RAII wrappers; define nothing
// Rule of Five example (manages raw pointer)
class Buffer {
    char* data_;
    size_t size_;
public:
    Buffer(size_t n) : data_(new char[n]), size_(n) {}
    ~Buffer() { delete[] data_; }

    // Copy
    Buffer(const Buffer& o) : data_(new char[o.size_]), size_(o.size_) {
        std::copy(o.data_, o.data_ + size_, data_);
    }
    Buffer& operator=(const Buffer& o) {
        Buffer tmp(o); std::swap(data_, tmp.data_); std::swap(size_, tmp.size_);
        return *this;
    }
    // Move
    Buffer(Buffer&& o) noexcept : data_(o.data_), size_(o.size_) {
        o.data_ = nullptr; o.size_ = 0;
    }
    Buffer& operator=(Buffer&& o) noexcept {
        std::swap(data_, o.data_); std::swap(size_, o.size_); return *this;
    }
};

Move semantics & value categories

11. What is the difference between lvalue and rvalue?

An lvalue has a persistent identity (an address you can take). An rvalue is a temporary value that does not persist beyond the expression.

int x = 5;
int* p = &x;     // x is lvalue — has address

int* q = &5;     // error: 5 is rvalue — no address

std::string s = std::string("hello");  // "hello" is rvalue

C++11 added rvalue references (&&) to distinguish temporaries and enable move semantics.

12. What is move semantics? Why is it important?

Move semantics allow "stealing" resources from an rvalue (temporary) instead of copying them, which is O(1) instead of O(n) for containers.

std::vector<int> make_vec() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    return v;   // RVO or move — no deep copy
}

std::vector<int> a = {1, 2, 3};
std::vector<int> b = std::move(a);  // a is now empty; b owns the data

std::move casts to rvalue reference — it does not itself move anything; it just enables the move constructor/assignment to be selected.

13. What is perfect forwarding?

Perfect forwarding passes arguments to another function while preserving their value category (lvalue stays lvalue, rvalue stays rvalue). It uses forwarding references (T&& in a deduced context) and std::forward.

template<typename T>
void wrapper(T&& arg) {
    target(std::forward<T>(arg));  // forwards lvalue as lvalue, rvalue as rvalue
}

This is the core technique in factory functions and std::make_unique/std::make_shared.


Templates

14. What are function templates and class templates?

Templates generate type-specific code at compile time — they are C++'s primary mechanism for generic programming.

// Function template
template<typename T>
T max_val(T a, T b) { return a > b ? a : b; }

auto m1 = max_val(3, 5);        // T deduced as int
auto m2 = max_val(3.0, 5.0);   // T deduced as double

// Class template
template<typename T, size_t N>
class Array {
    T data_[N];
public:
    T& operator[](size_t i) { return data_[i]; }
    size_t size() const { return N; }
};

Array<int, 4> arr;

15. What is template specialization?

Template specialization provides a custom implementation for a specific type.

// Primary template
template<typename T>
struct IsPointer { static constexpr bool value = false; };

// Full specialization for pointer types
template<typename T>
struct IsPointer<T*> { static constexpr bool value = true; };

static_assert(!IsPointer<int>::value);
static_assert(IsPointer<int*>::value);

Partial specialization (for class templates only) matches a pattern, e.g., all pointer types T*.

16. What is SFINAE?

SFINAE (Substitution Failure Is Not An Error): when template argument substitution fails, the compiler silently removes that overload from the candidate set instead of issuing an error.

#include <type_traits>

// Only enabled when T is integral
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
double_it(T x) { return x * 2; }

// Only enabled when T is floating-point
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
double_it(T x) { return x * 2.0; }

double_it(3);     // calls integral version
double_it(3.14);  // calls floating-point version

C++20 concepts replace SFINAE with clearer syntax.

17. What are variadic templates?

Variadic templates accept zero or more template arguments using the ... pack syntax.

// Print any number of arguments
template<typename T>
void print(T&& t) { std::cout << t << "\n"; }

template<typename T, typename... Args>
void print(T&& first, Args&&... rest) {
    std::cout << first << " ";
    print(std::forward<Args>(rest)...);  // recurse with remaining args
}

print(1, "hello", 3.14);  // 1 hello 3.14

C++17 fold expressions simplify many variadic patterns:

template<typename... Args>
auto sum(Args... args) { return (args + ...); }  // fold expression

STL containers and algorithms

18. When do you use vector vs list vs deque?

Container Access Insert/remove front Insert/remove back Insert/remove middle Memory
vector O(1) random O(n) O(1) amortized O(n) Contiguous
list O(n) O(1) O(1) O(1) with iterator Non-contiguous
deque O(1) random O(1) O(1) O(n) Chunked

Default: use vector. Use list only when you need stable iterators after insert/erase in the middle. deque for double-ended queues.

19. What is the difference between map and unordered_map?

Feature map unordered_map
Underlying structure Red-Black tree (BST) Hash table
Lookup O(log n) O(1) average, O(n) worst
Key order Sorted Unordered
Requires operator< std::hash<K> + operator==
Iterator stability Stable after insert Rehash invalidates all
Memory Pointer-per-node overhead Load-factor dependent

Use unordered_map for performance-critical lookups; map when ordered iteration matters.

20. What are iterators and iterator categories?

Iterators are objects that point to elements in a range and support traversal. Categories from weakest to strongest:

Category Operations Examples
Input ++, * (read once) istream_iterator
Output ++, * (write once) ostream_iterator
Forward ++, * (multi-pass) forward_list
Bidirectional ++, -- list, set
Random access ++, --, +n, -n, [] vector, deque
Contiguous (C++17) Random + elements in memory vector, array

Algorithms require minimum iterator categories; passing weaker ones fails to compile.

21. How does std::sort work and what does it require?

std::sort requires random access iterators and a strict weak ordering (i.e., < or a custom comparator that is irreflexive, asymmetric, and transitive). The standard mandates O(n log n) worst case; most implementations use introsort (quicksort + heapsort fallback).

std::vector<int> v = {5, 2, 8, 1};
std::sort(v.begin(), v.end());               // ascending
std::sort(v.begin(), v.end(), std::greater<>{}); // descending

// Custom comparator
std::sort(v.begin(), v.end(), [](int a, int b){ return a % 3 < b % 3; });

std::stable_sort preserves relative order of equal elements (O(n log² n) without extra memory).


Pointers, references, and const

22. What is the difference between a pointer and a reference?

Feature Pointer Reference
Null value Can be nullptr Cannot be null
Rebinding Can point to another object Cannot be rebound
Indirection *ptr to dereference Implicit dereference
Arithmetic Supports ptr+n Not supported
Use in optional Yes No

Use references for "always valid" aliasing; use pointers when null or rebinding is needed.

23. Explain const with pointers.

int x = 1, y = 2;
const int* p1 = &x;   // pointer to const int — cannot modify *p1
int* const p2 = &x;   // const pointer — cannot rebind p2, but can modify *p2
const int* const p3 = &x; // both const

p1 = &y;    // ok — rebind pointer
// *p1 = 5;  // error — *p1 is const
*p2 = 5;   // ok — can modify value
// p2 = &y;  // error — p2 is const

Tip: read right-to-left: const int* → "pointer to const int"; int* const → "const pointer to int".

24. What is constexpr and how does it differ from const?

const means the value cannot be changed after initialization, but it may be computed at runtime. constexpr guarantees computation at compile time.

const int x = 5;          // may be runtime
constexpr int y = 5;       // must be compile time

constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

constexpr int f5 = factorial(5);  // computed at compile time (120)
int arr[factorial(4)];             // ok — compile-time constant

C++20 expanded constexpr to allow more constructs (dynamic allocation, virtual functions).


Exception handling

25. How does C++ exception handling work?

try {
    if (condition) throw std::runtime_error("oops");
    // ...
} catch (const std::runtime_error& e) {
    std::cerr << e.what() << "\n";
} catch (const std::exception& e) {
    // catches all standard exceptions
} catch (...) {
    // catches anything
}

Exceptions propagate up the call stack until caught. Stack unwinding calls destructors of all local objects in the frames between throw and catch — this is why RAII is critical.

26. What is noexcept?

noexcept declares that a function will not throw. The compiler can generate more efficient code (especially for move operations), and callers can reason about exception safety.

void safe_function() noexcept { /* guaranteed not to throw */ }

// noexcept conditional
template<typename T>
void swap(T& a, T& b) noexcept(std::is_nothrow_move_constructible_v<T> &&
                                std::is_nothrow_move_assignable_v<T>) {
    T tmp = std::move(a);
    a = std::move(b);
    b = std::move(tmp);
}

If a noexcept function does throw, std::terminate is called. Move constructors should be noexcept to allow containers to use them (otherwise vector falls back to copy on reallocation).


Concurrency

27. How do you create and join threads in C++?

#include <thread>

void task(int id) { std::cout << "Thread " << id << "\n"; }

int main() {
    std::thread t1(task, 1);
    std::thread t2(task, 2);
    t1.join();  // wait for t1 to finish
    t2.join();
}

detach() runs the thread independently; you must ensure the thread does not access destroyed objects after main returns.

28. What is a race condition and how do you prevent it?

A race condition occurs when two threads access shared data concurrently and at least one access is a write.

#include <mutex>

int counter = 0;
std::mutex mtx;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);  // RAII lock
    ++counter;
}

Tools: std::mutex, std::atomic<T> (for simple types), std::shared_mutex (multiple readers / single writer).

29. What is std::atomic?

std::atomic<T> provides lock-free, thread-safe operations on a single object without explicit mutexes, for types like int, bool, and pointers.

#include <atomic>
std::atomic<int> count{0};

// Thread-safe without mutex
count.fetch_add(1, std::memory_order_relaxed);
int val = count.load(std::memory_order_acquire);

Memory orders (relaxed, acquire, release, seq_cst) control how operations are synchronized across CPUs.

30. What is std::condition_variable?

A condition variable lets threads wait for a condition to become true without busy-waiting.

#include <condition_variable>
#include <queue>

std::queue<int> q;
std::mutex mtx;
std::condition_variable cv;

// Producer
void produce(int val) {
    std::lock_guard<std::mutex> lock(mtx);
    q.push(val);
    cv.notify_one();
}

// Consumer
int consume() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return !q.empty(); });  // spurious-wake safe
    int val = q.front(); q.pop();
    return val;
}

Modern C++ (C++11 to C++20)

31. What is auto and when should you use it?

auto deduces the type from the initializer at compile time. Use it to:

  • Avoid long iterator types (auto it = v.begin())
  • Capture lambda types (which have no writable name)
  • Avoid accidentally copying a view or proxy object
auto x = 42;                    // int
auto d = 3.14;                  // double
auto it = vec.begin();          // std::vector<int>::iterator
auto f = [](int a){ return a*2; }; // lambda type

Avoid auto when the type is not obvious from the right-hand side; explicit types aid readability.

32. What is a lambda expression?

A lambda is an anonymous function object defined inline.

// [capture](params) -> return_type { body }
auto add = [](int a, int b) { return a + b; };
std::cout << add(3, 4) << "\n";  // 7

int offset = 10;
auto add_offset = [offset](int x) { return x + offset; };  // capture by value
auto mutate   = [&offset](int x) { offset += x; };         // capture by reference

Lambdas are commonly passed to algorithms:

std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; });

33. What are range-based for loops?

std::vector<int> v = {1, 2, 3};

for (int x : v)     { /* copy */ }
for (int& x : v)    { x *= 2; /* modify in-place */ }
for (const int& x : v) { /* read without copy */ }

// Works on any type with begin()/end()

34. What is nullptr?

nullptr is a type-safe null pointer constant introduced in C++11, replacing NULL (which is typically 0 or (void*)0).

void f(int*) { std::cout << "pointer\n"; }
void f(int)  { std::cout << "int\n"; }

f(nullptr);  // unambiguously calls f(int*)
f(NULL);     // might be ambiguous — NULL is 0 (int)

35. What are structured bindings (C++17)?

Structured bindings decompose aggregates (pairs, tuples, structs, arrays) into named variables.

auto [key, value] = std::make_pair("age", 30);
std::cout << key << ": " << value << "\n";

std::map<std::string, int> m = {{"alice", 1}, {"bob", 2}};
for (const auto& [k, v] : m) {
    std::cout << k << " = " << v << "\n";
}

36. What are concepts (C++20)?

Concepts are named requirements on template parameters, replacing complex SFINAE with readable constraints.

#include <concepts>

template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

template<Numeric T>
T square(T x) { return x * x; }

square(4);     // ok — int is Numeric
square(2.5);   // ok — double is Numeric
// square("hi"); // error: string is not Numeric

Standard concepts include std::same_as, std::derived_from, std::convertible_to, std::invocable, and std::ranges::range.

37. What are C++20 ranges?

Ranges provide composable, lazy sequence operations via the pipe | operator.

#include <ranges>
#include <vector>

std::vector<int> v = {1, 2, 3, 4, 5, 6};

auto result = v
    | std::views::filter([](int x){ return x % 2 == 0; })
    | std::views::transform([](int x){ return x * x; });

for (int x : result) std::cout << x << " ";  // 4 16 36

Views are lazy — no intermediate containers are created.


Object lifetime and copy control

38. What is the copy-and-swap idiom?

Copy-and-swap makes copy/move assignment operators exception-safe and eliminates duplication:

class Buffer {
    std::size_t size_;
    char* data_;
public:
    // ... constructor, destructor, copy constructor ...

    friend void swap(Buffer& a, Buffer& b) noexcept {
        using std::swap;
        swap(a.size_, b.size_);
        swap(a.data_, b.data_);
    }

    Buffer& operator=(Buffer other) {  // takes by value → copy already made
        swap(*this, other);           // steal resources from copy
        return *this;                 // other's destructor releases old data
    }
};

39. What is object slicing?

Slicing occurs when a derived-class object is assigned to a base-class variable by value, losing derived members.

struct Base { int x; };
struct Derived : Base { int y; };

Derived d{1, 2};
Base b = d;  // b.y is gone — sliced off

// Fix: use pointers or references
Base& ref = d;  // no slicing — ref sees Derived

Miscellaneous

40. What is undefined behavior (UB)?

Undefined behavior means the C++ standard places no requirements on what the program does. Common sources:

UB source Example
Signed integer overflow INT_MAX + 1
Null pointer dereference *nullptr
Use after free delete p; *p = 1;
Out-of-bounds access arr[10] when size is 5
Data race Two threads write to same non-atomic variable
Uninitialized variable read int x; return x;

Compilers may assume UB never occurs, leading to surprising optimizations. Use sanitizers (-fsanitize=address,undefined) in development.

41. What is the difference between static_cast, dynamic_cast, reinterpret_cast, and const_cast?

Cast Purpose Safe?
static_cast Compile-time checked conversions (numeric, up/downcasting with known type) Mostly
dynamic_cast Downcast through polymorphic hierarchy; returns nullptr/throws on failure Yes (runtime check)
reinterpret_cast Bit-level reinterpretation; pointer↔integer, unrelated pointer types Rarely
const_cast Remove const/volatile qualifier Only safe if original was non-const
// dynamic_cast example
Base* b = new Derived();
if (Derived* d = dynamic_cast<Derived*>(b)) {
    d->derived_method();
}

42. What is inline and when should you use it?

inline is a hint to the compiler to substitute the function body at the call site (avoiding function call overhead) and tells the linker to allow multiple identical definitions across translation units.

Modern compilers inline aggressively without the keyword; inline is more useful today for:

  • Header-only libraries: define a function in a header without violating the One Definition Rule (ODR).
  • inline constexpr variables (C++17): avoid ODR issues with constants in headers.

43. What are anonymous (unnamed) namespaces?

An unnamed namespace restricts the visibility of names to the current translation unit — similar to static at file scope, but also works for types and classes.

namespace {
    // Only visible in this .cpp file
    int helper(int x) { return x * 2; }
    struct InternalType { int data; };
}

Prefer unnamed namespaces over static for translation-unit-local declarations in C++.

44. What is the One Definition Rule (ODR)?

The ODR states:

  1. Any function, variable, class, or template may be defined at most once across the entire program (linker errors if violated).
  2. Types (classes, enums) and templates may be defined in multiple translation units if all definitions are identical (typically via headers).

Violations produce multiple definition linker errors. Mitigations: include guards (#pragma once), inline functions in headers, and unnamed namespaces.

45. How does std::string_view differ from std::string?

std::string_view (C++17) is a non-owning, read-only reference to a contiguous sequence of characters.

Feature std::string std::string_view
Owns memory Yes No
Copy cost O(n) O(1)
Mutable Yes No
Null-terminated Yes Not guaranteed
Use case Store and modify Read-only function parameters
void print(std::string_view sv) { std::cout << sv << "\n"; }

std::string s = "hello";
print(s);          // no copy
print("world");    // no heap allocation
print(s.substr(0, 3)); // temporary string_view

Watch out: string_view must not outlive the underlying buffer.


Design patterns & best practices

46. What is the Pimpl idiom?

Pimpl (Pointer to Implementation) hides private implementation details behind a forward-declared opaque pointer, reducing compilation dependencies and improving ABI stability.

// widget.h
class Widget {
public:
    Widget();
    ~Widget();
    void render();
private:
    struct Impl;
    std::unique_ptr<Impl> pImpl_;
};

// widget.cpp
struct Widget::Impl {
    // Implementation details hidden from header
    int data_;
    void heavy_function();
};

Widget::Widget() : pImpl_(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::render() { pImpl_->heavy_function(); }

47. What is a functor (function object)?

A functor is an object that overloads operator(), making it callable like a function. Functors carry state and can be passed to STL algorithms.

struct Multiplier {
    int factor;
    int operator()(int x) const { return x * factor; }
};

std::vector<int> v = {1, 2, 3};
std::transform(v.begin(), v.end(), v.begin(), Multiplier{3});
// v = {3, 6, 9}

Lambdas are syntactic sugar for anonymous functors. std::function<R(Args...)> erases the functor type.

48. What is CRTP (Curiously Recurring Template Pattern)?

CRTP provides static (compile-time) polymorphism without the vtable overhead:

template<typename Derived>
class Base {
public:
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

class Concrete : public Base<Concrete> {
public:
    void implementation() { std::cout << "Concrete impl\n"; }
};

Concrete c;
c.interface();  // no virtual dispatch

Used in Eigen (linear algebra), SFML, and many performance-critical libraries.

49. What is type erasure?

Type erasure hides concrete types behind a uniform interface at runtime — std::function, std::any, and std::variant are standard examples.

#include <functional>

// std::function erases the concrete callable type
std::function<int(int, int)> add = [](int a, int b){ return a + b; };
std::function<int(int, int)> mul = std::multiplies<int>{};

// std::any erases any type
#include <any>
std::any val = 42;
std::any str = std::string("hello");

50. What are common C++ anti-patterns to avoid?

Anti-pattern Problem Fix
Raw new/delete Leaks, exception-unsafety Use make_unique/make_shared
Missing virtual destructor UB when deleting via base pointer Always virtual ~Base() = default
Passing large objects by value Unnecessary copies Pass by const& or move
using namespace std; globally Name collisions Qualified names or local using
reinterpret_cast abuse UB, breaks strict aliasing Use proper abstractions
Not marking moves noexcept std::vector copies instead of moves Add noexcept to move ops
Signed/unsigned comparison warnings Subtle bugs Use size_t consistently or cast explicitly
catch (...) with no rethrow Swallows exceptions silently Log and rethrow, or handle specifically

Common mistakes

Mistake Why it's wrong Fix
delete vs delete[] mismatch UB for arrays Match new[] with delete[]
Dangling reference from temporary Lifetime ended Return by value or use const& to extend temp life
Iterator invalidation after push_back vector may reallocate Re-acquire iterators after mutation
Double free UB (heap corruption) Use smart pointers
Forgetting override keyword Silently fails to override Always use override
shared_ptr cycle Memory leak Use weak_ptr to break cycles
Copying instead of moving large objects Performance loss Use std::move for temporaries
UB on out-of-bounds Crashes, exploits Use at() or bounds checking

C++ vs other languages

Feature C++ Java Rust Python
Memory management Manual + RAII GC Ownership system GC
Zero-cost abstractions Yes Partial Yes No
Operator overloading Yes No Yes Yes
Templates/Generics Templates (CT) Generics (RT type erasure) Generics Duck typing
Concurrency primitives thread/atomic synchronized/volatile Mutex/channels GIL + threading
Undefined behavior Yes No No No
Compile speed Slow (templates) Moderate Slow N/A (interpreted)
Main domain Systems, games, HPC Enterprise, Android Systems, WASM Data, scripting

FAQ

Q: Should I learn C or C++ first?
A: Learn C++ directly if your goal is modern software development. C knowledge helps with embedded/OS work, but C++ is a superset and widely used in systems, games, and HPC.

Q: What is the difference between ++i and i++?
A: ++i increments and returns the new value. i++ increments and returns the old value (copies it first). For iterators and objects, ++i is preferred as i++ involves a copy.

Q: When should I use std::array instead of std::vector?
A: Use std::array when the size is known at compile time and you want stack allocation with bounds checking via .at(). std::vector is for runtime-sized, heap-allocated sequences.

Q: What is the difference between std::endl and "\n"?
A: std::endl flushes the output buffer in addition to inserting a newline — much slower in a loop. Prefer "\n" unless you specifically need to flush.

Q: How do I choose between inheritance and composition?
A: Prefer composition over inheritance (GoF principle). Use inheritance for true "is-a" relationships and when you need runtime polymorphism. Use composition for code reuse without the tight coupling of inheritance.

Q: What tools help write safer C++?
A: Enable warnings (-Wall -Wextra -Wpedantic), use sanitizers (-fsanitize=address,undefined), run static analysers (clang-tidy, Cppcheck), use smart pointers, and follow the C++ Core Guidelines.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools