C++ Interview Questions and Answers
23 hand-picked C++ interview questions with
detailed answers. Open the interactive version above to search, filter
by difficulty, run code, bookmark questions and track your progress.
What is C++ and how does it differ from C?
C++ is a multi-paradigm language (procedural, OOP, generic, functional) built as an extension of C. Key additions over C:
- Classes and OOP — encapsulation, inheritance, polymorphism.
- Templates — generic programming (STL).
- RAII — automatic resource management via destructors.
- Namespaces — avoid name collisions.
- Stronger type system — references, overloading, bool type.
C++ is compiled, performant, and used in game engines, browsers, databases, trading systems, and embedded systems.
Classes, objects, and encapsulation in C++.
A class defines attributes (member variables) and behaviours (member functions). An object is an instance of a class.
Encapsulation — hide internal state behind access specifiers:
public — accessible everywhere.private — only within the class.protected — class and subclasses.
class BankAccount {
private:
double balance;
public:
void deposit(double amount) { balance += amount; }
double getBalance() const { return balance; }
};
Constructors and destructors.
Constructor — called when an object is created. Same name as the class, no return type. Can be overloaded or defaulted.
Destructor — called when an object is destroyed (~ClassName()). Cleans up resources — the foundation of RAII (Resource Acquisition Is Initialization).
Compiler-generated default constructor/destructor exist if you don't define them (Rule of Zero).
class File {
FILE *f;
public:
File(const char *path) { f = fopen(path, "r"); }
~File() { if (f) fclose(f); } // RAII
};
Inheritance and access specifiers.
class Derived : public Base — Derived inherits Base's members. Access modes:
- public inheritance — is-a relationship (most common).
- protected inheritance — rare; public members become protected in Derived.
- private inheritance — implementation inheritance (has-a via inheritance).
Use override keyword on overridden virtual methods (C++11).
class Animal {
public:
virtual void speak() { cout << "..."; }
};
class Dog : public Animal {
public:
void speak() override { cout << "woof"; }
};
Compile-time vs runtime polymorphism.
Compile-time — function overloading (same name, different params) and templates. Resolved by the compiler.
Runtime — virtual functions enable dynamic dispatch via vtable. A base pointer/reference calls the derived version:
Animal* a = new Dog(); a->speak(); calls Dog::speak().
Requires virtual on the base method and a virtual destructor if deleting through base pointer.
class Base {
public:
virtual ~Base() = default;
virtual void draw() = 0; // pure virtual = abstract
};
Why must base class destructors be virtual?
If you delete a derived object through a base pointer and the destructor is not virtual, only the base destructor runs — derived members are not cleaned up (undefined behavior / memory leak).
With virtual ~Base(), the correct destructor chain runs: derived first, then base.
Base *obj = new Derived();
delete obj; // safe only if ~Base() is virtual
Rule of Three / Five / Zero.
If a class defines any of these, consider defining all:
- Rule of Three — destructor, copy constructor, copy assignment operator.
- Rule of Five (C++11) — add move constructor and move assignment.
- Rule of Zero — if all members manage themselves (smart pointers, STL containers), define none — compiler-generated versions are correct.
Needed when the class owns a raw resource (pointer, file handle).
class Buffer {
std::unique_ptr<int[]> data;
size_t size;
public:
Buffer(size_t n) : data(std::make_unique<int[]>(n)), size(n) {}
// no copy/move/destructor needed — Rule of Zero
};
unique_ptr vs shared_ptr vs weak_ptr.
- unique_ptr — exclusive ownership. Cannot copy; can move. Zero overhead vs raw pointer. Default choice.
- shared_ptr — shared ownership via reference counting. Thread-safe ref count. Use when multiple owners exist.
- weak_ptr — non-owning observer of a shared_ptr. Breaks circular reference cycles. Must lock() before use.
Prefer make_unique / make_shared over raw new.
auto p = std::make_unique<int>(42);
auto s = std::make_shared<std::string>("hello");
std::weak_ptr<std::string> w = s;
Move semantics and rvalue references.
rvalue reference (T&&) binds to temporaries and enables moving resources instead of copying.
std::move() casts to rvalue — doesn't move by itself, just enables move constructor/assignment to be called.
Moving a std::vector transfers the internal pointer — O(1) instead of O(n) copy. The source is left in a valid but empty state.
std::vector<int> a = {1,2,3};
std::vector<int> b = std::move(a); // a is now empty
// a.size() == 0, b has {1,2,3}
std::vector — the most important STL container.
A dynamic array — contiguous memory, O(1) random access, amortized O(1) push_back. Automatically grows capacity.
Key methods: push_back, emplace_back (constructs in place), size, capacity, reserve (pre-allocate), begin/end for iteration.
std::vector<int> v;
v.reserve(1000);
for (int i = 0; i < 1000; i++) v.push_back(i);
map vs unordered_map vs set.
- map — sorted key-value pairs (red-black tree). O(log n) lookup. Iteration in key order.
- unordered_map — hash table. O(1) average lookup. No ordering.
- set — sorted unique keys. unordered_set — hash-based unique keys.
Choose map when you need ordering; unordered_map for speed when order doesn't matter.
std::unordered_map<std::string, int> freq;
freq["hello"]++;
std::map<std::string, int> sorted;
sorted["zebra"] = 1;
sorted["apple"] = 2; // iterates apple, zebra
What are templates in C++?
Templates enable generic programming — write code once that works with any type. The compiler generates specific versions at compile time.
- Function templates —
template<typename T> T max(T a, T b) - Class templates —
std::vector<T>, std::map<K,V>
Templates are resolved at compile time — zero runtime overhead (monomorphization).
template<typename T>
T max(T a, T b) { return (a > b) ? a : b; }
int m = max(3, 7); // int version
double d = max(3.1, 2.9); // double version
References vs pointers in C++.
| Reference | Pointer |
|---|
| Syntax | int& r = x; | int* p = &x; |
| Null | Cannot be null | Can be nullptr |
| Reassign | Cannot rebind | Can point elsewhere |
| Arithmetic | No | Yes (pointer++) |
| Use | Function params, aliases | Dynamic memory, optional values |
void swap(int& a, int& b) {
int tmp = a; a = b; b = tmp;
}
const correctness in C++.
const int x — cannot modify x.const int* p — can't modify data through p (pointer to const).int* const p — can't change what p points to (const pointer).const int* const p — neither.void foo() const — member function won't modify object state.
Mark methods const when they don't modify state — enables calling on const objects.
class Circle {
double radius;
public:
double area() const { return 3.14 * radius * radius; }
};
Operator overloading.
C++ lets you define custom behaviour for operators on user-defined types. Common overloads: operator+, operator==, operator<< (stream output), operator[] (indexing).
Cannot overload: ::, .*, ?:, sizeof. At least one operand must be a user-defined type.
struct Point { int x, y; };
bool operator==(const Point& a, const Point& b) {
return a.x == b.x && a.y == b.y;
}
Lambda expressions in C++.
Anonymous function objects. Syntax: [capture](params) { body }
[=] — capture all by value.[&] — capture all by reference.[x, &y] — mixed capture.
Used heavily with STL algorithms: sort, for_each, find_if.
std::vector<int> v = {3, 1, 4, 1, 5};
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b; // descending
});
Exception handling in C++.
throw raises an exception; try/catch handles it. Catch by reference (catch(const std::exception& e)) to avoid slicing.
Standard hierarchy: std::exception → runtime_error, logic_error, out_of_range, etc.
noexcept marks functions that won't throw — enables compiler optimizations.
try {
if (n < 0) throw std::invalid_argument("negative");
} catch (const std::exception& e) {
std::cerr << e.what();
}
Namespaces in C++.
Namespaces group names to avoid collisions. std is the standard library namespace.
using std::cout; — import one name.using namespace std; — import all (avoid in headers).- Anonymous namespace — internal linkage (like static at file scope).
namespace math {
double pi = 3.14159;
double area(double r) { return pi * r * r; }
}
// use: math::area(5.0)
new/delete vs smart pointers — when to use raw new?
new/delete allocate/deallocate single objects; new[]/delete[] for arrays. In modern C++, avoid raw new/delete in application code.
Use instead:
make_unique / make_shared for heap objects.std::vector instead of new[].- Stack allocation when lifetime is function-scoped.
// avoid: MyClass* p = new MyClass();
// prefer:
auto p = std::make_unique<MyClass>();
Abstract classes and pure virtual functions.
A class with at least one pure virtual function (= 0) is abstract — cannot be instantiated. Acts as an interface defining a contract subclasses must fulfil.
Used for polymorphic designs: plugin systems, strategy pattern, factory pattern.
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0; // pure virtual
};
class Circle : public Shape {
double r;
public:
double area() const override { return 3.14 * r * r; }
};
STL algorithms overview.
<algorithm> provides generic functions operating on iterator ranges:
- sort, stable_sort — O(n log n) sorting.
- find, find_if — linear search.
- transform — apply function to each element.
- accumulate — reduce/sum a range.
- count, count_if — count matching elements.
- lower_bound, binary_search — on sorted ranges.
std::vector<int> v = {5, 2, 8, 1};
std::sort(v.begin(), v.end());
auto it = std::lower_bound(v.begin(), v.end(), 5);
auto keyword and range-based for loops.
auto — compiler deduces the type. Use for verbose iterator types and lambdas. auto& for non-copying references; const auto& for read-only.
Range-for — iterate any container with begin/end:
for (const auto& item : container)
std::vector<std::string> names = {"Ada", "Bob"};
for (const auto& name : names) {
std::cout << name << '\n';
}
What is a friend function/class?
friend grants a non-member function or another class access to a class's private and protected members. Breaks encapsulation deliberately — use sparingly.
Common uses: overloading operator<< for stream output, tightly coupled helper classes.
class Point {
int x, y;
friend std::ostream& operator<<(std::ostream& os, const Point& p);
};