C++ Interview Questions and Answers
Pointers, memory management, the STL, templates and modern C++.
Whether you are preparing for entry-level C++ interview questions for freshers or senior software engineer interview questions addressing concurrency, scalability, and system architecture, this track provides peer-reviewed model answers with syntax walkthroughs, edge cases, and practical interview tips.
1 What is the difference between a pointer and a reference? Easy
A pointer is a variable that stores an address. It can be null, reassigned and used with pointer arithmetic. A reference is an alias for an existing object: it must be initialised when created, cannot be reseated, and is never null.
int a = 1, b = 2;
int* p = &a; // p can point elsewhere
*p = 10; // writes a
p = nullptr; // allowed
int& r = a; // r aliases a
r = b; // assigns b's value into a; rebinding is impossible
Use references for parameters and return values when a value must exist; use pointers when "nothing" is a valid state, for optional output parameters, or for dynamic data structures. Prefer references by default for safety and clearer intent. const T& is the idiomatic way to pass large objects without copying. Note that references are usually implemented as pointers under the hood, and sizeof a reference gives the referenced type's size, not a pointer's.
2 How do stack and heap allocation differ in C++? Easy
The stack is a LIFO region managed automatically: allocation is a pointer bump, extremely fast, and memory is reclaimed when the scope ends. The heap is a large pool managed by new/delete or smart pointers; allocation is slower, can fragment, and you must free it.
void f() {
int x = 5; // stack
int* p = new int(5); // heap
std::unique_ptr<int> q =
std::make_unique<int>(5); // heap, RAII-managed
delete p; // must free manually
} // x and q cleaned automatically
Stack size is limited (often 1-8MB), so large arrays or deep recursion overflow it. Heap objects can outlive the scope that created them and support dynamic sizes.
Prefer stack allocation and RAII containers such as std::vector and std::string, and reach for the heap only when you need a lifetime beyond the scope, polymorphic ownership, or a runtime-determined size. This avoids leaks and fragmentation.
3 What do constructors and destructors do? Easy
A constructor runs when an object is created and initialises its members. A destructor runs when it is destroyed and releases resources. Constructors share the class name and have no return type; destructors are ~Class(), take no arguments and there is exactly one.
class Buffer {
public:
explicit Buffer(std::size_t n)
: data_(new char[n]), size_(n) {}
~Buffer() { delete[] data_; }
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
private:
char* data_;
std::size_t size_;
};
Prefer the member initialiser list over assignment in the constructor body. It initialises directly instead of default-constructing then assigning, and it is required for const and reference members. Initialisation order follows declaration order, not the order in the list, which can surprise you.
A destructor that reliably frees resources is what makes RAII work, so cleanup happens on scope exit and during stack unwinding from an exception.
4 What is RAII and why is it central to modern C++? Medium
RAII, Resource Acquisition Is Initialisation, binds a resource's lifetime to an object's lifetime: acquire in the constructor and release in the destructor. Because destructors run on scope exit, including during stack unwinding from an exception, resources cannot leak.
void write(const std::string& path) {
std::ofstream out(path); // opened in ctor
std::lock_guard<std::mutex> lock(m);
out << "data\n";
} // lock released, file closed, even if << throws
The standard library is built on RAII: std::string, std::vector, std::fstream, std::lock_guard and std::unique_ptr. Writing a small RAII wrapper for a C API handle, socket or mutex is idiomatic and gives exception safety.
The alternative, manual cleanup with goto or duplicated error paths, is error-prone and leaks on early returns. The "rule of zero" extends RAII: compose members that manage their own resources so your class needs no custom destructor or copy/move operations at all.
5 When should you use unique_ptr, shared_ptr or weak_ptr? Medium
Raw new and delete are error-prone, so C++11 introduced smart pointers in <memory> that express ownership.
std::unique_ptr<T>: exclusive ownership, cannot be copied, is movable, and has essentially zero overhead versus a raw pointer. Create withstd::make_unique.std::shared_ptr<T>: shared ownership through an atomic control block with a reference count; the object is destroyed when the last owner goes away. Create withstd::make_shared. Heavier, and the count is thread-safe but the object is not.std::weak_ptr<T>: a non-owning observer of a shared object, used to break cycles.lock()yields ashared_ptror null.
auto a = std::make_unique<Widget>();
auto b = std::make_shared<Widget>();
std::weak_ptr<Widget> w = b;
if (auto s = w.lock()) {
s->use();
}
Prefer unique_ptr by default, upgrade to shared_ptr only for genuine shared ownership, and never create two shared pointers from the same raw pointer, which causes a double free.
6 How do virtual functions, vtables and virtual destructors work? Medium
A virtual function enables runtime polymorphism. Each polymorphic class gets a vtable of function pointers and each object stores a hidden vptr. A call through a base pointer looks up the most-derived override at runtime.
struct Base {
virtual ~Base() = default;
virtual void speak() const { std::cout << "base"; }
};
struct Derived : Base {
void speak() const override { std::cout << "derived"; }
};
Base* p = new Derived();
p->speak(); // "derived"
delete p; // needs a virtual destructor
If the base destructor is not virtual, deleting through a base pointer is undefined behaviour and derived members are not destroyed, leaking resources. Any base intended for polymorphic deletion needs a virtual destructor.
Mark overrides with override so signature mismatches become compile errors, and use final to prevent further overriding and enable devirtualisation. Virtual calls cannot be inlined and add an indirection, so do not make everything virtual; keep interfaces small and stable.
7 How do move semantics and rvalue references improve performance? Medium
Copying large objects is expensive. Move semantics transfer ownership of a resource from a temporary (an rvalue) instead of copying, leaving the source in a valid but unspecified state. It relies on rvalue references (T&&) plus a move constructor and move assignment operator.
std::vector<std::string> make();
std::vector<std::string> v = make(); // move, not copy
std::string s = "long text";
std::vector<std::string> w;
w.push_back(std::move(s)); // s is left empty-ish
std::move is just a cast to an rvalue reference; it does not move anything by itself. Moves of standard containers are O(1) pointer swaps, not element-by-element copies.
The "rule of five" says that if you define a destructor, copy or move operation, define or delete all of them. Mark move operations noexcept so containers such as vector prefer them during reallocation. Never use a moved-from object except to assign or destroy it.
8 What is const correctness and why does it matter? Medium
const is a compile-time contract. A const object cannot be modified, and a const member function promises not to modify the object, so it can be called on const objects and through const references.
class Account {
public:
double balance() const { return balance_; }
void deposit(double x) { balance_ += x; }
private:
double balance_ = 0;
};
void print(const Account& a) {
std::cout << a.balance();
}
Use const T& parameters to avoid copies without allowing mutation. const can qualify the pointed-to data or the pointer itself: const char* const.
Use mutable for members that must change in a const method, such as caches or mutexes. Casting away const with const_cast is undefined behaviour if the object was originally const.
Const correctness is easiest to adopt early because it propagates through an API. It documents intent, enables compiler optimisation, and prevents accidental modification of shared data.
9 How do templates, specialization and concepts work? Hard
Templates parameterise code by type and are instantiated at compile time, giving zero-cost abstraction.
template <typename T>
T maxOf(T a, T b) { return a > b ? a : b; }
template <>
const char* maxOf<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) > 0 ? a : b; // explicit specialization
}
Variadic templates and fold expressions handle arbitrary argument counts. C++20 concepts constrain templates and produce readable diagnostics:
template <typename T> requires std::integral<T>
T twice(T x) { return x + x; }
Template definitions usually live in headers because the compiler needs them at instantiation. The downsides are longer compile times, code bloat and historically terrible error messages. auto and decltype help deduction, while static_assert turns assumptions into clear compile-time checks. Prefer concepts over SFINAE for readability in modern code.
10 Explain the rule of three, five and zero, plus undefined behaviour. Hard
The rule of three: if a class needs a custom destructor, copy constructor or copy assignment operator, it probably needs all three, because they manage the same resource. The compiler-generated copy does a shallow member-wise copy, so forgetting one causes double frees or leaks.
class Buf {
public:
~Buf(); // frees
Buf(const Buf&); // deep copy
Buf& operator=(const Buf&);
Buf(Buf&&) noexcept; // C++11 additions
Buf& operator=(Buf&&) noexcept;
};
The rule of five extends this to the move operations for efficiency. The rule of zero says the best design needs none of them: store resources in RAII members such as std::vector or unique_ptr and let the compiler generate correct operations.
Undefined behaviour means the standard imposes no requirements, so out-of-bounds access, use-after-free, signed overflow and data races let the optimiser do surprising things. Detect it with -Wall -Wextra, AddressSanitizer, UBSan and Valgrind rather than guessing.
Frequently Asked Questions About C++ Interviews
What do hiring managers evaluate in C++ technical rounds?
Technical interviewers look for foundational fluency, idiomatic syntax, clarity when communicating complex logic, and awareness of performance trade-offs (e.g. memory footprint, render performance, and network latency) in production environments.
What are the best interview tips for practicing C++ questions?
Use active recall: summarize each answer in your own words before revealing the model solution. Focus on explaining why a certain approach is chosen rather than just memorizing code syntax.