interviewDeck

Your one-stop platform to prepare, practice and ace your interviews.

Loading your questions…

All Questions

Filters & tools

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.

Runtimevirtual 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 templatestemplate<typename T> T max(T a, T b)
  • Class templatesstd::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++.

ReferencePointer
Syntaxint& r = x;int* p = &x;
NullCannot be nullCan be nullptr
ReassignCannot rebindCan point elsewhere
ArithmeticNoYes (pointer++)
UseFunction params, aliasesDynamic 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::exceptionruntime_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);
};