experienced

Experienced interview questions

A virtual function is a member function in a base class that a derived class is allowed to override with its own version, and the correct version to run is decided at runtime based on the actual type of the object being used, rather than the type of the pointer or reference pointing at it. This enables polymorphism, letting a single piece of code that works with a base class pointer correctly call the most specific overridden behaviour for whatever derived object it actually happens to be holding.

C++

A vtable, short for virtual table, is a hidden table of function pointers that the compiler generates for any class containing virtual functions, with each entry pointing to the correct implementation for that class. Every object of such a class secretly carries a pointer to its class's vtable, and calling a virtual function actually means looking up the right implementation through that table at runtime. This lookup is what makes runtime polymorphism possible, at the cost of a small amount of extra memory and an indirect function call.

C++

A pure virtual function is a virtual function declared in a base class with no implementation at all, marked in a way that forces every concrete derived class to supply its own version before it can be used. A class containing at least one pure virtual function becomes an abstract class, meaning it can never be instantiated directly and exists purely to define a shared interface that derived classes are required to implement, guaranteeing a consistent set of behaviours across all of them.

C++

The diamond problem occurs when a class inherits from two separate base classes that both themselves inherit from a single common ancestor, meaning the final derived class ends up with two separate copies of that ancestor's data, one through each inheritance path. This creates real ambiguity: referring to a member from the shared ancestor becomes unclear about which of the two copies is actually meant, and the compiler will typically refuse to compile such an ambiguous reference until it is resolved.

C++

Declaring the inheritance from the common ancestor as virtual tells the compiler that, no matter how many separate paths lead back to that same ancestor, only a single shared copy of it should ever exist in the final derived object. This removes the ambiguity entirely, since there is now exactly one copy of the shared base class's data rather than several competing ones. It does add some additional complexity and a small runtime cost, so it is typically reached for only when this specific inheritance shape is genuinely unavoidable.

C++

RAII stands for resource acquisition is initialization, a pattern where acquiring a resource, such as memory, a file handle, or a lock, happens inside a constructor, and releasing it happens automatically inside the matching destructor. Because destructors in C++ run reliably and automatically whenever an object goes out of scope, wrapping any resource this way guarantees it gets cleaned up correctly, even if an exception is thrown partway through, without the programmer needing to remember a manual cleanup step at every possible exit point.

C++

A smart pointer is an object that wraps around a raw pointer and automatically manages the lifetime of whatever it points to, releasing that memory once it is no longer needed, following the same RAII idea used for other resources. Unlike a raw pointer, which requires the programmer to remember exactly when and how to release memory manually, a smart pointer handles that bookkeeping on its own, dramatically reducing the chance of memory leaks or accidentally using memory that has already been freed.

C++

A unique ownership smart pointer guarantees that only one such pointer can ever own a given piece of memory at a time, and ownership can be transferred but never duplicated, which keeps its overhead extremely low. A shared ownership smart pointer instead allows several pointers to jointly own the same memory, internally tracking how many owners currently exist and only releasing the memory once the very last owner disappears. Shared ownership is more flexible but carries a small additional cost from that internal reference counting.

C++

A weak ownership smart pointer refers to memory managed by a shared ownership smart pointer without itself counting as an owner, meaning it does not keep that memory alive on its own. It is primarily used to break a reference cycle, a situation where two shared owners point at each other and would otherwise keep each other alive forever, and to safely check whether the memory they refer to still exists before attempting to use it, without ever forcing that memory to stay alive artificially.

C++

Move semantics allow the resources owned by a temporary or otherwise disposable object, such as memory it has allocated internally, to be transferred directly into a new object instead of being fully copied and then discarded. Rather than duplicating potentially large amounts of data, ownership of the underlying resource is simply handed over, leaving the original object in a valid but essentially empty state. This can dramatically improve performance for operations that would otherwise involve expensive, unnecessary copying of large objects.

C++

An rvalue reference is a reference type specifically designed to bind to temporary values that are about to be destroyed anyway, rather than to a normal, named, long lived variable. It is what makes move semantics possible, since a function can detect through an rvalue reference parameter that the object it received is temporary and safe to plunder for its resources, rather than needing to make a full, expensive copy of something that was going to disappear immediately afterward regardless.

C++

The rule of three states that if a class needs to explicitly define any one of a custom destructor, a custom copy constructor, or a custom copy assignment operator, it almost certainly needs to define all three of them. This is because needing one of these usually signals the class is managing some resource, like raw memory, whose default copying behaviour, a simple bit for bit copy, would be incorrect and could cause serious problems for the other two operations as well.

C++

The rule of five extends the older rule of three to also include the move constructor and the move assignment operator, both introduced when move semantics were added to the language. If a class manages a resource and needs custom copy behaviour, it typically also benefits from custom move behaviour for efficiency, so all five special member functions, the destructor, copy constructor, copy assignment, move constructor, and move assignment, are usually considered together as a complete, consistent set.

C++

The rule of zero suggests that, whenever possible, a class should avoid managing raw resources directly at all, and should instead compose itself out of existing types, such as smart pointers and standard containers, that already correctly handle their own construction, destruction, copying, and moving. Following this approach, a class needs to define none of the five special member functions itself, since the compiler generated defaults will correctly forward all of that responsibility to the well behaved member objects it is built from.

C++

Template specialization lets a programmer provide a separate, customized implementation of a template for one particular type or set of types, instead of relying on the single generic version that would otherwise be generated for every type. This is useful when the generic implementation would be technically correct but inefficient or awkward for a specific type, letting that one case be handled with genuinely tailored logic while every other type continues to use the ordinary, general purpose version of the template.

C++

A function template defines a single generic function whose parameter and return types are filled in automatically by the compiler based on whatever arguments are actually passed when it is called. A class template instead defines an entire generic class, and the specific type it should work with generally has to be stated explicitly when an object of that class is created. The well known vector container is itself a class template, since it can hold integers, strings, or objects of any other type depending on how it is declared.

C++

The Standard Template Library is a large collection of ready made, generic containers, iterators, and algorithms that ship as part of the C++ standard library. Containers such as vector and map store and organise data, iterators provide a uniform way to step through the contents of any container, and algorithms such as sort or find operate generically on any container through those same iterators. Together these three pieces let common data handling tasks be written once and reused across essentially any type of data.

C++

An iterator is an object that behaves similarly to a pointer, letting code step through the elements of a container one at a time without needing to know the specific internal details of how that particular container stores its data. Because every standard container provides iterators that behave in a broadly consistent way, the exact same generic algorithm, such as sorting or searching, can operate correctly on a vector, a list, or many other container types, without needing a separate version written specifically for each one.

C++

A vector stores its elements in one contiguous block of memory, which makes accessing any element by position extremely fast and also plays very well with the processor's cache, but inserting or removing an element in the middle requires shifting every element after it. A linked list stores elements scattered across memory, connected by pointers, making insertion and removal at any position fast without shifting anything, but accessing an arbitrary position requires walking through the list one link at a time, which is comparatively slow.

C++

Operator overloading is commonly used to make a custom type behave naturally alongside the built in operators programmers already expect, such as overloading the plus sign so two custom vector or matrix objects can be added together directly, or overloading the equality comparison so two custom objects can be compared meaningfully with a familiar syntax. It is also frequently used to overload the stream insertion operator, allowing a custom object to be printed directly to the console in a readable, well formatted way.

C++

Exception safety describes how gracefully a piece of code behaves when an exception is thrown partway through its execution. The basic guarantee promises the program remains in some valid, non corrupted state, without guaranteeing anything was actually accomplished. The strong guarantee promises that either the entire operation fully succeeds or it has no visible effect at all, leaving everything exactly as it was before. The no throw guarantee is the strongest, promising the operation will never throw an exception in the first place.

C++

Static linking copies the actual code of any libraries a program depends on directly into the final executable file at build time, producing one larger, self contained file that needs nothing extra to run. Dynamic linking instead keeps library code in separate shared files on disk, and the program locates and loads them only when it actually starts running. Dynamic linking produces smaller executables and lets multiple programs share one copy of a library in memory, while static linking avoids depending on the correct library version being present on the target machine.

C++

Undefined behaviour refers to code whose outcome the C++ language standard deliberately places no requirements on whatsoever, meaning a compiler is technically free to make it do absolutely anything, including something that appears to work correctly, crash outright, or silently produce subtly wrong results. Common causes include reading past the end of an array, using an uninitialized variable, or dereferencing a pointer that no longer points to valid memory. Because the effects are unpredictable and can vary between compilers, undefined behaviour is generally treated as a serious bug that must be eliminated entirely.

C++

A dangling pointer is a pointer that still holds the address of memory that has already been freed or has otherwise gone out of scope, even though the pointer itself was never updated to reflect that. Using it afterward, either reading through it or writing through it, reaches into memory that may have already been reused for something completely different, producing unpredictable and often very hard to reproduce bugs. A common cause is returning a pointer to a local variable from a function, since that variable is destroyed the instant the function returns.

C++

A memory leak occurs when a program allocates memory on the heap but loses every way of reaching it again, usually because the one pointer that referenced it was overwritten, went out of scope, or the corresponding delete call was simply forgotten, without the memory ever being released back to the system. In a long running program, repeated leaks gradually consume more and more available memory over time, eventually degrading performance or causing the program to run out of memory entirely. Smart pointers largely prevent this class of bug by automating the cleanup.

C++

Stack unwinding is the process that occurs when an exception is thrown and no matching handler exists in the current function, causing the current function to exit immediately while properly running the destructors of any local objects it had created so far, before control passes up to the function that called it. This process repeats, function after function, until a matching catch block is finally found somewhere up the call chain, or the program terminates if none exists anywhere along the way.

C++

A lambda expression lets a programmer define a small, unnamed function directly at the point where it is needed, without having to write a separate, fully named function elsewhere just to use it once. This is especially convenient when passing a short piece of custom behaviour into a standard algorithm, such as describing exactly how two elements should be compared when sorting a collection, right there inline where the sorting call itself is written, keeping closely related code physically close together.

C++

A capture list, written at the very start of a lambda expression, specifies which variables from the surrounding, enclosing scope the lambda is allowed to use inside its own body, and whether each one is captured by value, as an independent copy, or by reference, giving direct access to the original variable. Without listing a variable in the capture list, or capturing everything with a suitable shorthand, the lambda simply cannot see or use that outside variable at all from within its body.

C++

A mutex is the actual low level synchronization object that only one thread can hold, or lock, at any given time, used to protect a piece of shared data from being accessed by more than one thread simultaneously. A lock guard is a small wrapper object that locks a mutex the moment it is created and automatically unlocks it the moment it goes out of scope, following the same RAII pattern used elsewhere in C++, which prevents the easy mistake of forgetting to manually unlock a mutex after use.

C++

A data race occurs when two or more threads access the same piece of shared memory at the same time, at least one of them is writing to it, and there is no synchronization mechanism, such as a mutex, coordinating that access between them. The resulting behaviour becomes genuinely undefined, potentially producing corrupted values, inconsistent results, or crashes that can be extremely difficult to reproduce reliably, since the exact outcome often depends on precise timing that can differ from one run of the program to the next.

C++

A C++ program first goes through preprocessing, where directives such as file inclusion and macro substitution are resolved into plain source code. Compiling then translates that source code into low level object code specific to the target machine, one source file at a time. Linking finally combines all the separate object files together, along with any required libraries, resolving references between them to produce a single, complete, runnable executable file. Each stage can fail independently, and error messages usually indicate which one is responsible.

C++

A preprocessor directive, always beginning with a hash symbol, is processed before actual compilation even begins and works purely as text substitution, such as inserting the contents of an included header file or replacing a macro name with its defined value everywhere it appears. A regular statement, by contrast, is genuine C++ code that the compiler itself understands, checks for correctness, and eventually translates into machine instructions. Preprocessor directives have no real awareness of C++ syntax or types at all, since they operate purely on raw text.

C++

Name mangling is the process by which a C++ compiler encodes extra information, such as a function's parameter types and the class it belongs to, directly into the internal name used for that function within the compiled object file. This allows the linker to correctly distinguish between several overloaded functions that share the same visible name in the source code but differ in their parameters. It also explains why C++ code often needs a special declaration to interoperate cleanly with code compiled using the plain C convention, which does not mangle names at all.

C++

A shallow copy duplicates an object's member variables exactly as they are, meaning that if the original object held a pointer to some external data, the copy ends up holding a pointer to that very same external data rather than its own independent version. A deep copy instead also duplicates whatever that pointer refers to, producing a genuinely independent copy with no shared underlying data at all. Classes managing their own raw resources typically need a custom copy constructor to perform a proper deep copy correctly.

C++

A copy constructor is a special constructor that creates a new object as a copy of an existing object of the very same class, and it is called automatically whenever an object is passed by value into a function, returned by value from one, or explicitly initialized directly from another existing object. If a class does not define its own copy constructor, the compiler quietly generates a default one that performs a simple shallow copy, which is only correct for classes that do not manage any external resources themselves.

C++

A move constructor creates a new object by transferring ownership of resources away from a temporary or otherwise disposable object, rather than duplicating that data the way an ordinary copy constructor would. It typically works by simply copying an internal pointer or handle from the source object and then resetting the source to a safe, empty state, avoiding the cost of an actual data copy entirely. This is particularly valuable for objects that manage large amounts of dynamically allocated memory internally.

C++

Inheritance models an is a relationship, where a derived class genuinely is a more specific kind of its base class and automatically gains its behaviour. Composition instead models a has a relationship, where one class simply contains another class as a member variable and reuses its behaviour by explicitly calling into it, without pretending to actually be that other class. Composition is often favoured over inheritance in modern C++ design because it tends to produce more flexible, less tightly coupled relationships between classes.

C++

A static member variable belongs to the class itself rather than to any single object of that class, meaning every object shares exactly one common copy of it, and it exists even before any object has been created. A static member function similarly belongs to the class as a whole, can be called without needing an actual object to call it on, and can only access other static members directly, since it has no particular object's data of its own to work with.

C++

Overloading means defining several functions that share the same name but differ in their parameter lists, and the compiler decides which one to call based purely on the arguments provided, resolved entirely at compile time. Overriding means a derived class provides its own specific implementation of a virtual function already defined in its base class, keeping exactly the same name and parameters, and the correct version to actually run is decided at runtime based on the real type of the object involved.

C++

Polymorphism means that the same piece of code, such as a single function call written once, can behave differently depending on the actual type of object it is operating on at that moment. C++ achieves this kind of runtime polymorphism through virtual functions: calling a virtual function through a base class pointer or reference automatically invokes whichever specific version the real, underlying object's class has overridden, decided dynamically at the moment the program actually runs, rather than being fixed once and for all at compile time.

C++