C++ Interview Questions 2026

C++ interviews have a different flavor from most language-specific rounds, a lot of it comes down to memory, who owns what, when does it get freed, what happens if you get it wrong. You can write working C++ for years and still get tripped up explaining exactly why a shallow copy breaks, or what a vtable actually is, because the language mostly lets you get away with a fuzzy mental model right up until an interviewer asks you to be precise about it.

This page goes through the stuff that actually comes up: pointers and references (and why they're not just two ways to write the same thing), the Rule of Three/Five, the STL containers people actually use day to day, smart pointers, and enough multithreading to hold a real conversation about race conditions. If you're prepping for a product company, an embedded role, or a competitive-programming-heavy DSA round, this is the language a lot of that still runs on.

Why C++ Still Shows Up So Much in Interviews

C++ has been the default language for DSA prep and competitive programming for a long time, and it's still the backbone of a lot of systems, games, embedded software, trading systems, that genuinely can't afford a garbage collector pausing at the wrong moment. Interviewers lean on C++ questions because they reveal something Java or Python questions often don't:

  • Whether you actually understand memory, not just "the framework handles it"
  • Whether you can reason about performance and undefined behavior instead of trusting the runtime to save you
  • Whether you know the STL well enough to pick the right container instead of defaulting to a vector for everything
  • Whether you understand what's happening at the object level, construction, destruction, copying, moving, not just how to call new

Who This Page Is For

  • Students and freshers prepping for DSA-heavy interviews at product companies
  • Backend and systems engineers interviewing for performance-sensitive roles
  • Anyone who's used C++ for competitive programming but never had to explain the memory model out loud

C++ Interview Questions and Answers (2026)

C++ Fundamentals

1. What's the actual difference between C and C++? People say "C++ is just C with classes" but that undersells it a lot.

C is a procedural language, no built-in concept of objects, no function overloading, no templates, no exceptions, you structure everything around functions and plain data (structs). C++ started as an extension of C but grew into its own thing, OOP, templates, the STL, RAII, exception handling, and a much stronger type system. Calling it "C with classes" was fair in the early 90s, today it undersells just how differently you actually write idiomatic modern C++ compared to C, smart pointers and RAII alone change how you think about resource management at a pretty fundamental level.

2. What's the difference between a compiler and a linker, and where does an "undefined reference" error actually come from?

The compiler translates your source code, one file at a time, into object code (machine instructions), but it doesn't know or care about symbols defined in other files, it just trusts they'll show up eventually and leaves a placeholder. The linker's job is to take all those separately compiled object files (and libraries) and stitch them together, resolving every one of those placeholders to an actual address. An "undefined reference" happens specifically at the linking stage, you declared something (a function, a variable) and the compiler was happy to trust you, but the linker searched everywhere it was told to look and genuinely couldn't find where it's actually defined.

3. What's the difference between declaration and definition in C++? Why does this distinction matter for header files?

A declaration just tells the compiler something exists and what its type/signature is, without allocating storage or providing an implementation, int add(int, int); is a declaration. A definition actually provides the implementation or allocates the storage, int add(int a, int b) { return a + b; } is a definition. This matters for header files because you generally want to put declarations in headers (so multiple .cpp files can include the header and know a function exists) but definitions in exactly one .cpp file, if you put a full function definition in a header and include that header in multiple .cpp files, you'll get a "multiple definition" linker error, since now the linker finds the same symbol defined more than once.

4. What's the difference between pass by value, pass by reference, and pass by pointer?

Pass by value copies the argument into the function's parameter, so changes inside the function never affect the caller's original variable, which is safe but can be expensive for large objects since you're paying for a full copy. Pass by reference (int& x) gives the function direct access to the original variable, no copy, and changes inside the function do affect the caller's variable, it also can't be null and doesn't need to be re-bound. Pass by pointer (int* x) is similar to reference in that it avoids a copy, but it explicitly can be null, and it requires dereferencing inside the function, giving you more explicit control (and more ways to mess it up) than a reference.

5. What does const actually do in different positions, like const int*, int* const, and const int* const?

Read it right to left from the variable name and it stops being confusing: const int* p is a pointer to a const int, you can change what p points to, but you can't modify the int through p. int* const p is a const pointer to a regular int, you can modify the int through p, but you can't make p point somewhere else after initialization. const int* const p combines both, p can neither be reassigned nor used to modify the value it points to, it's genuinely locked down in both directions.

6. What's the difference between #define and const, or between a macro and an inline function?

#define is a preprocessor directive, it does pure textual substitution before compilation even starts, the compiler never even sees the macro name, just whatever it got substituted with, which means no type checking and it can produce genuinely confusing bugs (operator precedence issues if you forget parentheses around a macro's arguments). const creates an actual, typed, scoped variable that the compiler is fully aware of, with real type checking. Similarly, a macro-based "function" is just blind text substitution with no type safety, while an inline function is a real function the compiler understands and type-checks, but hints to the compiler that it may be worth inlining directly at the call site to avoid function call overhead.


Memory Management: Pointers, References and the Heap

1. What's the actual difference between a pointer and a reference? This gets asked constantly and the answer is more subtle than "a reference is just a pointer with different syntax."

A reference must be initialized when it's declared and can never be reassigned to refer to something else afterward, it's bound to one object for its entire lifetime, and it can never be null. A pointer can be declared without initialization, reassigned to point at something else at any time, and can be null, or even point to invalid memory entirely. Under the hood a reference is often implemented using a pointer, but the language enforces stricter rules around it, which is exactly why references are generally preferred when you don't need pointer arithmetic or the ability to be null or reseated, they're safer by construction.

2. What's the difference between stack memory and heap memory in a C++ program, and why is stack allocation so much faster?

Stack memory is used for local variables and function call frames, it grows and shrinks automatically as functions are entered and exited, allocation is literally just moving a stack pointer, which is why it's extremely fast, and cleanup is automatic when a function returns. Heap memory is allocated explicitly (with new) and persists until you explicitly free it (with delete) or it goes out of scope via a smart pointer, allocation involves the memory allocator searching for a suitable free block, which is meaningfully slower and comes with real bookkeeping overhead compared to the stack's simple pointer bump.

3. What happens if you forget to call delete on something you allocated with new? Walk through what actually leaks.

The memory you allocated stays marked as "in use" for the rest of the program's lifetime, since nothing ever tells the operating system it's free to reclaim, even though your program has lost every reference to it and can never access or free it again. That's a memory leak, and in a long-running program (a server, a service that stays up for days), leaking memory repeatedly, say, inside a loop that runs on every request, means memory usage climbs steadily over time until the process eventually gets killed for running out of memory, or the whole machine starts thrashing.

void leaky() {
    int* data = new int[1000];
    // ... use data ...
    // forgot delete[] data; here, this memory is gone for good
}

4. What's the difference between new/delete and malloc/free? Why do C++ style guides pretty much always say use new instead of malloc?

malloc/free are C-style memory functions, they just allocate and free raw bytes, no concept of constructors or destructors, no type safety, they return void* which needs an explicit cast. new/delete are C++ operators that actually call the appropriate constructor when allocating an object and the destructor when deallocating it, and they're type-safe, returning the correct pointer type directly. Mixing the two (allocating with malloc but freeing with delete, or vice versa) is undefined behavior, since they're not guaranteed to be interchangeable, which is exactly why the advice is to pick one convention, new/delete for idiomatic C++, and stay consistent.

5. What is a dangling pointer, and how is it different from a memory leak? People conflate these two.

A memory leak is memory you can no longer reach or free, but it's still technically valid, allocated memory, just orphaned. A dangling pointer is the opposite kind of problem, it's a pointer that still points to a memory address, but that memory has already been freed (or the object it pointed to has gone out of scope), so the pointer is now referring to memory that's no longer valid, using it (dereferencing it) is undefined behavior, it might crash immediately, might silently return garbage, or might appear to work fine until it doesn't, which is exactly what makes dangling pointer bugs so nasty to track down.

6. What is a null pointer dereference, and why does it usually crash instead of just quietly failing?

Dereferencing a null pointer means trying to read or write through a pointer that isn't actually pointing at any valid object, it's pointing at address zero, which no valid object ever legitimately occupies. On most modern operating systems, address zero (and a range around it) is deliberately left unmapped specifically so that dereferencing null triggers an immediate, loud segmentation fault instead of silently corrupting some unrelated part of memory, it's actually a deliberate safety mechanism, a crash you can debug from a stack trace is far better than silent, hard-to-trace memory corruption.


Constructors, Destructors and Object Lifecycle

1. What's the difference between a default constructor, a parameterized constructor, and a copy constructor?

A default constructor takes no arguments and sets up an object with default/initial state, the compiler generates one for you automatically if you don't define any constructor yourself. A parameterized constructor takes arguments, letting you initialize an object's state directly at creation with specific values instead of default ones. A copy constructor specifically takes a reference to an existing object of the same type and initializes a new object as a copy of it, ClassName(const ClassName& other), and it's what actually runs whenever you initialize one object directly from another, not just when you obviously call it yourself.

2. When does the copy constructor actually get called? People are surprised how often it fires without them writing new SomeClass explicitly.

It fires whenever an object is initialized from another object of the same type, which includes more places than people expect: passing an object by value into a function, returning an object by value from a function (though modern compilers often optimize this away via copy elision), and directly initializing one object from another, MyClass b = a;. It doesn't fire on plain assignment to an already-existing object, that's the copy assignment operator instead, a genuinely common source of confusion since the syntax can look deceptively similar.

3. What is the Rule of Three (and Rule of Five)? Why does defining one of destructor/copy constructor/copy assignment usually mean you need all of them?

The Rule of Three says if a class needs a custom destructor, copy constructor, or copy assignment operator, it almost certainly needs all three, because needing any one of them is a strong signal the class is managing some resource (raw memory, a file handle) that the compiler's default, member-wise versions won't handle correctly. The Rule of Five extends this for modern C++, adding the move constructor and move assignment operator, since if you're managing a resource explicitly, you generally want efficient move semantics too, not just correct copying, otherwise you're stuck with unnecessary expensive copies where a cheap move would've worked.

4. What's the difference between a shallow copy and a deep copy? What actually breaks if a class doing a shallow copy has a raw pointer member?

A shallow copy copies a member's value directly, so if that member is a pointer, the new object ends up holding a copy of the same pointer, pointing at the exact same underlying memory as the original, not a separate copy of the data itself. A deep copy actually allocates new memory and copies the pointed-to data itself, so the two objects have entirely independent copies. If a class relies on the compiler's default (shallow) copy behavior but has a raw pointer member that it deletes in its destructor, you get a double-free bug, both the original and the copy have destructors that try to delete the very same memory address, which is undefined behavior and a classic, painful crash to debug.

5. Why should destructors in a base class almost always be declared virtual? What specifically goes wrong if you forget?

If you delete a derived object through a base class pointer and the base class destructor isn't virtual, only the base class's destructor runs, the derived class's destructor is silently skipped entirely. If that derived class has its own resources to clean up, memory it allocated, a file it opened, none of that cleanup happens, it's a memory/resource leak specifically triggered by polymorphic deletion through a base pointer, a genuinely subtle bug since the code compiles fine and often "seems" to work until you actually notice resources aren't being freed.

class Base {
public:
    virtual ~Base() {}   // without virtual, deleting via Base* leaks Derived's resources
};
class Derived : public Base {
    int* data = new int[100];
public:
    ~Derived() { delete[] data; }
};

6. What is RAII, and how does it actually tie object lifetime to resource management?

RAII (Resource Acquisition Is Initialization) is the idea that a resource, memory, a file handle, a mutex lock, should be acquired in an object's constructor and released in its destructor, tying the resource's lifetime directly to the object's scope. Because C++ guarantees destructors run automatically when an object goes out of scope, even if an exception is thrown, RAII gives you deterministic, automatic cleanup without manually remembering to call a "close" or "free" function yourself, it's the underlying principle behind smart pointers, std::lock_guard, and std::fstream all cleaning up after themselves automatically.


OOP in C++

1. What's the difference between function overloading and function overriding in C++?

Overloading means having multiple functions with the same name but different parameter lists within the same scope, resolved entirely at compile time based on the arguments you pass. Overriding means a derived class provides its own implementation of a virtual function it inherited from a base class, and which version actually runs is decided at runtime based on the object's real type, not the pointer or reference's declared type, that runtime resolution is what enables real polymorphism.

2. What does the virtual keyword actually do at the implementation level? What is a vtable?

When a class has at least one virtual function, the compiler generates a vtable (virtual table), essentially an array of function pointers, one entry per virtual function, and every instance of that class carries a hidden pointer (the vptr) to its class's vtable. When you call a virtual function through a base class pointer or reference, the call is resolved indirectly, at runtime, by looking up the correct function through that object's actual vtable, rather than being hardcoded at compile time to the base class's version, which is exactly the mechanism that makes runtime polymorphism actually work.

3. What's the difference between a pure virtual function and a regular virtual function? What does declaring one actually do to a class?

A regular virtual function has an implementation in the base class, which derived classes can optionally override, if they don't, the base version runs. A pure virtual function (virtual void foo() = 0;) has no implementation in the base class (or an optional one that must still be explicitly invoked), and declaring even one pure virtual function makes the entire class abstract, meaning you can no longer instantiate that class directly, only derive from it, and any concrete derived class is required to actually implement every pure virtual function before it can be instantiated itself.

4. What's the difference between public, private, and protected inheritance? This is a genuinely C++-specific wrinkle most other OOP languages don't have.

With public inheritance (by far the most common), the base class's public and protected members keep their same access level in the derived class, this is what models a genuine "is-a" relationship. With protected inheritance, the base's public members become protected in the derived class. With private inheritance, both public and protected base members become private in the derived class, meaning further-derived classes can't access them at all, this is used to model "implemented in terms of" rather than a true "is-a" relationship, and it's rare enough in practice that a lot of C++ developers go years without needing it, but interviewers like asking about it specifically because it doesn't exist in languages like Java or C#.

5. What is the diamond problem in multiple inheritance, and how does virtual inheritance actually solve it?

The diamond problem happens when a class inherits from two base classes that both, in turn, inherit from the same common base class, without virtual inheritance, the most-derived class ends up with two separate copies of that common base class's members, which is both wasteful and genuinely ambiguous, which copy does obj.member even refer to? Virtual inheritance (class B : virtual public A) fixes this by ensuring only one shared instance of the common base class exists in the final object, regardless of how many inheritance paths lead to it, resolving the ambiguity at the cost of some added complexity in how the object is laid out in memory.

6. What's the difference between composition and inheritance, and why do a lot of experienced C++ developers lean toward composition?

Inheritance models an "is-a" relationship, a derived class extends and specializes a base class. Composition models a "has-a" relationship, a class holds an instance of another class as a member and delegates to it rather than inheriting its interface directly. Experienced C++ developers often lean toward composition because deep inheritance hierarchies in C++ come with real, specific costs, virtual dispatch overhead, the fragile base class problem where changing a base class can silently break derived classes, and the genuine complexity of multiple/virtual inheritance discussed above, composition tends to produce more flexible, more easily testable, and less tightly coupled code.


STL: Containers, Iterators and Algorithms

1. What's the difference between a vector and a list, and how do you actually decide which one fits a given use case?

std::vector stores elements contiguously in memory, like a dynamically resizable array, giving you O(1) random access by index and excellent cache locality, but inserting or removing from the middle is O(n) since everything after has to shift. std::list is a doubly linked list, insertion and removal at any known position is O(1), but there's no random access at all, getting to the nth element means walking the list, O(n), and it has worse cache performance since nodes are scattered in memory rather than contiguous. In practice, vector is the right default for most use cases, cache locality tends to win in real-world performance even when the "big O" for some operation technically favors list, you'd reach for list specifically when you're doing a lot of insertion/removal in the middle of a large sequence and genuinely don't need random access.

2. What's the difference between std::map and std::unordered_map, and why would you ever pick the ordered one if the hash map is faster?

std::map is typically implemented as a balanced binary search tree (a red-black tree), giving O(log n) lookup, insertion, and deletion, but critically, it keeps keys in sorted order, and iterating it visits keys in that sorted order automatically. std::unordered_map is a hash table, averaging O(1) for those same operations (worse case O(n) with bad hash collisions), but with no ordering guarantee at all, iteration order is essentially arbitrary. You'd pick map over the faster unordered_map specifically when you actually need sorted iteration, range queries (finding all keys between X and Y), or a strict, predictable ordering, the extra log factor is a fair trade for genuinely needing that ordering guarantee.

3. What's an iterator, really, and why does the STL use them instead of just letting you index into every container directly?

An iterator is an object that abstracts "a position within a container" and supports a consistent interface for moving through it (typically ++, dereferencing with *, and comparison), regardless of the container's actual internal structure. The STL uses iterators specifically because not every container supports direct indexing, you can't do myList[5] efficiently on a std::list, but you can always advance an iterator through it. Iterators let generic algorithms (std::sort, std::find, std::accumulate) work uniformly across completely different container types, without needing a separate version of every algorithm for every container.

4. What's the difference between push_back and emplace_back on a vector? This is a genuinely underused optimization.

push_back takes an already-constructed object (or something that needs to be converted into one) and copies or moves it into the vector's storage. emplace_back takes the constructor arguments directly and constructs the object in place, directly inside the vector's memory, avoiding a separate temporary object and an extra copy/move step entirely. For simple types the difference is negligible, but for a class with an expensive constructor and copy, emplace_back(args...) can be a genuinely meaningful performance win over push_back(SomeClass(args...)), which is why a lot of experienced C++ code defaults to emplace_back habitually.

std::vector<std::pair<int, std::string>> v;
v.push_back(std::make_pair(1, "hello"));   // constructs a temporary, then copies/moves it in
v.emplace_back(1, "hello");                 // constructs the pair directly in place

5. What happens if you erase an element from a vector while iterating over it with a regular for loop? Why does this crash so often?

Erasing an element from a vector invalidates iterators at or after the erased position, since everything after it shifts to fill the gap, if you're holding onto an iterator or index from before the erase and keep using it as if nothing happened, you're now referencing either the wrong element or, past the new end, memory that's no longer valid, which is exactly the kind of undefined behavior that sometimes "happens to work" in testing and then crashes unpredictably in production. The correct pattern is to use the iterator that erase() itself returns (which points to the next valid element) to continue iterating, rather than manually incrementing a now-invalidated iterator.

6. What's the difference between std::array and a raw C-style array?

A C-style array (int arr[10]) decays to a raw pointer when passed to a function, losing its size information entirely, has no bounds checking, and doesn't support useful member functions like .size(). std::array<int, 10> is a lightweight wrapper that behaves like a fixed-size array with the same performance characteristics (no heap allocation, stored inline), but it retains its size information, works properly with STL algorithms and iterators, and doesn't decay to a pointer the way a raw array does, it's essentially "get the safety and convenience of a normal container, with the same zero-overhead performance as a raw array."


Templates and Generic Programming

1. What is a template in C++, and how is it actually different from generics in a language like Java or C#?

A template lets you write code, a function or a class, that's parameterized by type, and the compiler generates a completely separate, concrete version of that code for every distinct type it's actually used with, this is called template instantiation, and it happens entirely at compile time. Java/C# generics, by contrast, are largely implemented through type erasure (Java) or a shared runtime representation (C#), where there's typically one actual compiled version handling multiple types, with type information mostly enforced at compile time but not baked into separate machine code per type. This is why C++ templates can do things generics generally can't, like specializing behavior differently per type, at the cost of potentially larger compiled binaries (one copy of the code per type actually used).

2. What's the difference between a function template and a class template?

A function template lets a single function definition work across multiple types, the compiler figures out (or you specify) the actual type based on the arguments passed, template<typename T> T max(T a, T b). A class template does the same thing for an entire class, letting you define something like a generic Stack<T> once, and instantiate it for whatever specific type you need, Stack<int>, Stack<std::string>, each instantiation is effectively its own separate, independently compiled class under the hood.

3. What is template specialization, and when would you actually need it?

Template specialization lets you provide a completely different implementation of a template for a specific type (or a specific pattern of types), instead of relying on the generic version behaving correctly for that type by default. You'd need it when the generic implementation genuinely doesn't work correctly, or isn't optimal, for a particular type, a classic textbook example is specializing a generic swap<T> for bool differently than the generic pointer-swap logic, or specializing a container template differently for bool (which std::vector<bool> famously does, storing bits instead of full booleans).

4. What's the difference between compile-time polymorphism (templates) and runtime polymorphism (virtual functions)?

Compile-time polymorphism, achieved through templates (and function overloading), is resolved entirely by the compiler before the program even runs, there's no runtime cost, no vtable lookup, the compiler generates the specific code needed for each type used. Runtime polymorphism, achieved through virtual functions, is resolved dynamically, at runtime, based on the actual object's type, via the vtable mechanism, which has a small but real runtime cost (an indirect function call) but gives you flexibility templates can't, the actual type being called isn't known until the program is actually running, which is essential when you genuinely don't know the concrete type ahead of time, like handling a collection of different derived objects through base class pointers.

5. What is SFINAE, and why does it sound scarier than it actually is once you've seen one real example?

SFINAE stands for "Substitution Failure Is Not An Error," it's a rule that when the compiler is trying different template overloads and substituting in a specific type causes an invalid expression, the compiler doesn't treat that as a hard compile error, it just quietly removes that particular overload from consideration and tries the next candidate instead. In practice, this is what lets you write templates that only compile successfully for types that actually support a given operation (say, a template that only works for types with a .size() method), silently falling back to a different overload for types that don't, without SFINAE, you'd just get a hard compile error the moment any invalid substitution was attempted, with no graceful fallback possible.


Smart Pointers and Modern C++

1. What problem do smart pointers actually solve that raw pointers don't?

Raw pointers require you to manually remember to call delete at exactly the right time, not too early (dangling pointer), not too late or never (memory leak), and exception safety makes that manual bookkeeping genuinely hard to get right consistently, an exception thrown between new and delete skips the delete entirely unless you're extremely careful. Smart pointers wrap a raw pointer and use RAII, the destructor automatically deletes the managed object when the smart pointer itself goes out of scope, whether that's through normal control flow or an exception unwinding the stack, which eliminates an entire class of manual memory management bugs by construction.

2. What's the difference between unique_ptr, shared_ptr, and weak_ptr?

unique_ptr represents sole, exclusive ownership, only one unique_ptr can own a given object at a time, it can be moved (transferring ownership) but not copied, and it has essentially zero overhead compared to a raw pointer. shared_ptr allows multiple owners, using reference counting, the underlying object is destroyed only once the last shared_ptr referencing it goes out of scope, which comes with real overhead (an atomic reference count that needs updating). weak_ptr doesn't participate in ownership or the reference count at all, it's a non-owning observer of an object managed by a shared_ptr, used specifically to check whether that object still exists without keeping it alive itself.

std::unique_ptr<Widget> w = std::make_unique<Widget>();
std::shared_ptr<Widget> s1 = std::make_shared<Widget>();
std::shared_ptr<Widget> s2 = s1;   // now two owners, ref count is 2

3. What is a reference cycle with shared_ptr, and why does weak_ptr exist specifically to break it?

If object A holds a shared_ptr to object B, and object B holds a shared_ptr back to object A, each object's reference count is being kept alive by the other, so even if nothing outside that pair references either of them anymore, their reference counts never drop to zero, and neither object is ever destroyed, a genuine memory leak despite using "smart" pointers. weak_ptr exists specifically to break this, if one side of that mutual relationship holds a weak_ptr instead of a shared_ptr, it doesn't contribute to the reference count, so once nothing external references either object, the cycle can actually be broken and both get properly destroyed.

4. What's the difference between std::move and just copying an object? What is a move constructor actually doing under the hood?

Copying duplicates an object's entire state, allocating new resources and copying data over, which can be expensive for objects managing heap memory or other resources. std::move doesn't actually move anything by itself, it's just a cast that treats an object as an rvalue, signaling "you can steal this object's internal resources instead of copying them, since I'm not going to use it again." A move constructor takes advantage of that signal by directly transferring ownership of the source object's internal pointers/resources to the new object, and leaving the source in a valid but empty/default state, no new allocation, no copying of the actual data, just a cheap pointer handoff.

5. What are lambda expressions in C++, and what's actually happening when you capture a variable by value versus by reference?

A lambda is an anonymous, inline function object, useful for passing small pieces of behavior directly where they're needed, like a custom comparator to std::sort, without defining a separate named function or functor class. Capturing by value ([x]) copies the variable's current value into the lambda's internal state at the point the lambda is created, so later changes to the original variable don't affect what the lambda sees. Capturing by reference ([&x]) instead stores a reference to the original variable, so the lambda always sees its current, live value, including any changes made after the lambda was created, but this also means you need to be careful the referenced variable is still alive whenever the lambda actually gets called.

6. What's the difference between auto and explicitly writing out a type? Does auto make C++ dynamically typed?

No, auto doesn't change C++'s type system at all, it's purely a compile-time convenience, the compiler infers the exact concrete type from the initializer expression, and from that point on the variable is just as strictly, statically typed as if you'd written the type out explicitly yourself, it just saves you from typing out long, often verbose type names (especially common with iterators and template types) and reduces the risk of accidentally mismatching a type you wrote by hand against what an expression actually returns.


Exception Handling and Operator Overloading

1. What's the actual cost of using exceptions in C++? Why do some performance-critical codebases avoid them entirely?

When no exception is actually thrown, modern C++ exception handling has essentially zero runtime cost on the "happy path," that's a real design goal of the language's exception model. But when an exception is thrown, stack unwinding and the actual exception-handling machinery are comparatively expensive, and, maybe more importantly for some domains, the worst-case timing becomes unpredictable, which is a genuine problem for hard real-time systems (some embedded and game engine code) that need deterministic, bounded execution time and can't tolerate an unpredictable spike whenever an exception happens to be thrown, that's the real reason some codebases ban exceptions outright, not that they're inherently "slow."

2. What's the difference between a stack unwind during a normal function return and during an exception? Why does RAII matter so much here specifically?

During a normal return, the compiler generates straightforward cleanup code, running destructors for local objects in reverse order of construction, as part of the function simply exiting. During exception propagation, the runtime has to walk back up through every stack frame between where the exception was thrown and where it's actually caught, running destructors for every local object along that entire path, not just the immediate function, this is exactly why RAII matters so much in exception-heavy code, if your resource cleanup relies on manual code at the end of a function rather than a destructor, an exception thrown partway through will skip right past that manual cleanup entirely, but a destructor is guaranteed to run regardless of how the function's scope is exited.

3. Why is it good practice to catch exceptions by reference instead of by value?

Catching by value causes the exception object to be copied when it's caught, and if the exception is a derived class caught as its base type, that copy can slice the object, discarding derived-class-specific data, and it also means any custom, meaningful behavior from the derived class's overridden methods gets lost since you're now working with a plain base-class copy. Catching by reference (catch (const std::exception& e)) avoids that copy and the associated slicing entirely, preserving the exception object's actual, full derived type and behavior, which is why catch (const SomeException&) is the standard, recommended pattern over catch (SomeException e).

4. What is operator overloading, and why do you have to be careful with something like overloading the assignment operator?

Operator overloading lets you define custom behavior for operators (+, ==, =, and so on) when applied to your own class types, letting objects of your class be used with natural, familiar syntax instead of forcing users to call named methods for everything. Overloading assignment (operator=) specifically requires extra care because it needs to correctly handle self-assignment (a = a;), release whatever resources the object currently owns before taking on new ones (to avoid leaking), and return a reference to *this to support chained assignment, getting any one of these wrong is a classic source of subtle bugs, especially the self-assignment case, which is easy to overlook until it actually happens.

5. What's the difference between overloading the pre-increment and post-increment operators? Why does the syntax for distinguishing them look so weird?

Pre-increment (++x) increments the value and then returns a reference to the now-updated object itself, it's implemented as operator++() with no parameters. Post-increment (x++) needs to return the object's value before it was incremented, so it typically makes a copy of the current state first, then increments the actual object, then returns that saved copy by value, it's implemented as operator++(int), where that unused int parameter exists purely as a compiler convention to distinguish it from the pre-increment overload, it's never actually used inside the function itself, it's just a syntactic marker.


Multithreading and Performance in C++

1. What's the difference between a process and a thread, and how does C++'s std::thread actually create a new OS thread?

A process is an independent, isolated execution environment with its own memory space, two processes can't directly access each other's memory without explicit inter-process communication. A thread is a unit of execution within a process, and multiple threads within the same process share that process's memory space directly, which makes communication between them cheap but also makes it easy to accidentally corrupt shared data if you're not careful about synchronization. std::thread is a thin, portable C++ wrapper that, under the hood, makes the appropriate OS-specific system call (like pthread_create on Linux) to actually spin up a genuine, native OS-level thread, it's not a lightweight "green thread" or coroutine, it's a real OS thread.

2. What is a race condition, and how does std::mutex actually prevent one?

A race condition happens when multiple threads access and modify shared data concurrently without proper synchronization, and the final outcome ends up depending on the unpredictable timing of exactly how their operations happen to interleave, which can produce subtly wrong (or occasionally very wrong) results that are notoriously hard to reproduce reliably, since they depend on timing, not just logic. std::mutex prevents this by acting as a lock, only one thread can successfully "hold" the mutex at a time, any other thread attempting to lock it blocks and waits until the current holder releases it, which lets you wrap access to shared data in a lock/unlock pair to guarantee only one thread modifies it at any given moment.

std::mutex mtx;
int counter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);   // RAII: unlocks automatically on scope exit
    ++counter;
}

3. What's the difference between a mutex and a condition variable? Why do you usually need both together for producer-consumer style code?

A mutex only handles mutual exclusion, ensuring only one thread accesses shared data at a time, but it has no built-in way for a thread to efficiently wait for some specific condition to become true, "wait until the queue actually has an item in it," without a condition variable, you'd have to busy-wait in a loop, repeatedly locking, checking, and unlocking, wasting CPU cycles. A condition variable lets a thread efficiently sleep until another thread explicitly notifies it that something worth checking again has happened, which is exactly the producer-consumer pattern, a consumer thread waits on the condition variable when the queue is empty, and the producer thread notifies it after actually adding an item, so the consumer wakes up only when there's genuinely something to do, rather than constantly polling.

4. What is undefined behavior in C++, and why does the language have so much more of it than a language like Java?

Undefined behavior means the language standard places absolutely no requirements on what happens, it might crash, might silently produce wrong results, might appear to work perfectly fine, and the compiler is fully allowed to assume undefined behavior simply never happens, which it sometimes uses to justify aggressive optimizations that can make the bug even more unpredictable. C++ has considerably more of it than Java specifically because C++ prioritizes performance and gives you direct, low-level control (raw pointers, manual memory management, no mandatory bounds checking on arrays), Java trades away some of that raw performance and control specifically to guarantee things like array bounds checks and a managed memory model that eliminate whole categories of undefined behavior by design, at the cost of some runtime overhead C++ isn't willing to pay by default.

5. What's the difference between compile-time and runtime cost in C++? Why do people say "you don't pay for what you don't use"?

This is one of C++'s core design philosophies, features that add runtime overhead (like virtual functions, exceptions, or RTTI) should only cost you anything if you actually use them, a program that never uses virtual functions shouldn't pay any vtable overhead anywhere, unlike some languages where certain runtime costs are baked in universally regardless of whether a given program needs that feature at all. This is exactly why templates (resolved entirely at compile time, generating specialized code per type) are generally preferred over runtime polymorphism when you don't specifically need the runtime flexibility, you get the abstraction benefit without paying an indirect function call cost at runtime for something that could've been decided once, at compile time.



Struggling to Find a Job? Get Specific Batch Wise job Updates ✅ Check now

Join our WhatsApp Channel for more resources.