C++ course menu
Intermediate C++ Interview Questions and Answers
Intermediate C++ interview questions with answers and examples: virtual functions and vtables, virtual destructors, the Rule of Three/Five/Zero, shallow vs deep copy, smart pointers, map vs unordered_map, vector growth, iterator invalidation, static members, and exceptions.
On this page
- Object-oriented C++
- 1. How do virtual functions work?
- 2. Why does a base class need a virtual destructor?
- 3. What is object slicing?
- 4. What is the difference between overloading and overriding?
- 5. What are the Rule of Three, Rule of Five and Rule of Zero?
- 6. What is the difference between a shallow copy and a deep copy?
- 7. What is a static member?
- Memory and ownership
- 8. Compare uniqueptr, sharedptr and weak_ptr.
- 9. What is RAII?
- 10. What is the difference between new/delete and malloc/free?
- The standard library
- 11. map vs unordered_map: when would you use each?
- 12. How does std::vector grow, and what is the cost of push_back?
- 13. What is iterator invalidation?
- 14. What happens when an exception is thrown?
- Short coding questions
- 15. Count the frequency of each word in a sentence.
- 16. Implement a class with a static counter of live objects.
- Next steps
These questions test whether you understand how C++ works underneath: object lifetimes, polymorphism, memory ownership and the standard containers. Interviewers look for precise answers backed by a reason.
Object-oriented C++#
1. How do virtual functions work?#
Show solution
A virtual function is chosen at run time based on the actual type of the object, when called through a base-class reference or pointer:
#include <iostream>
#include <memory>
struct Shape {
virtual double area() const { return 0; }
virtual ~Shape() = default;
};
struct Square : Shape {
double side;
explicit Square(double s) : side(s) {}
double area() const override { return side * side; }
};
int main() {
std::unique_ptr<Shape> s = std::make_unique<Square>(3);
std::cout << s->area() << '\n';
}9Typical implementation: each class with virtual functions has a vtable (a table of function pointers), and every object holds a hidden pointer (the vptr) to its class's table. A virtual call looks up the function in that table, costing one extra indirection, and preventing inlining in most cases.
2. Why does a base class need a virtual destructor?#
Show solution
If a derived object is deleted through a base-class pointer and the base destructor is not virtual, only the base destructor runs. That is undefined behaviour, and in practice leaks whatever the derived part owns. Rule: any class with virtual functions, or intended to be deleted polymorphically, needs virtual ~Base() = default;.
3. What is object slicing?#
Show solution
Copying a derived object into a base-class object (not a reference or pointer) keeps only the base part, and virtual calls on the copy use the base implementation:
#include <iostream>
struct Animal {
virtual const char* sound() const { return "..."; }
virtual ~Animal() = default;
};
struct Dog : Animal {
const char* sound() const override { return "Woof"; }
};
int main() {
Dog d;
Animal sliced = d; // copy: slices off Dog
const Animal& ref = d; // reference: no slicing
std::cout << sliced.sound() << ' ' << ref.sound() << '\n';
}... WoofPass polymorphic objects by reference or (smart) pointer.
4. What is the difference between overloading and overriding?#
Show solution
- Overloading: several functions with the same name and different parameters in the same scope, resolved at compile time.
- Overriding: a derived class redefines a virtual function with the same signature, resolved at run time. Mark it
overrideso the compiler checks the signature matches.
5. What are the Rule of Three, Rule of Five and Rule of Zero?#
Show solution
- Rule of Three: if a class needs a user-defined destructor, copy constructor or copy assignment operator, it probably needs all three, because it manages a resource.
- Rule of Five: with C++11, add the move constructor and move assignment operator.
- Rule of Zero: prefer designing classes whose members (
std::vector,std::string,std::unique_ptr) manage resources themselves, so you need to write none of the five.
6. What is the difference between a shallow copy and a deep copy?#
Show solution
A shallow copy copies the pointer, so both objects share the same resource; a deep copy duplicates the resource itself. A class holding a raw owning pointer with the compiler-generated copy constructor makes shallow copies, and both destructors then delete the same memory (a double delete). Fix: implement a deep-copying copy constructor and assignment, or better, hold the data in a std::vector/std::string, whose copies are already deep.
#include <iostream>
#include <vector>
struct Gradebook {
std::vector<int> marks; // copying Gradebook copies the vector deeply
};
int main() {
Gradebook a{{90, 80}};
Gradebook b = a;
b.marks[0] = 10;
std::cout << a.marks[0] << ' ' << b.marks[0] << '\n';
}90 107. What is a static member?#
Show solution
A static data member belongs to the class, not to any object, so all objects share one copy. A static member function has no this pointer and can be called as ClassName::function(). Common uses: counters of created objects, shared configuration, and factory functions.
Memory and ownership#
8. Compare unique_ptr, shared_ptr and weak_ptr.#
Show solution
std::unique_ptr: sole ownership, no overhead compared with a raw pointer, movable but not copyable.std::shared_ptr: shared ownership through a reference count; the object is destroyed when the last owner goes away. Costs a control block and atomic count updates.std::weak_ptr: a non-owning observer of ashared_ptr-managed object, used to break reference cycles; calllock()to use it.
Default to unique_ptr; use shared_ptr only for genuinely shared ownership. Create them with std::make_unique / std::make_shared.
9. What is RAII?#
Show solution
"Resource Acquisition Is Initialization": a resource (memory, file, lock, socket) is acquired in an object's constructor and released in its destructor. Because destructors run automatically when objects go out of scope, including during exception unwinding, resources cannot leak. std::vector, std::fstream, smart pointers and std::lock_guard are all RAII types.
10. What is the difference between new/delete and malloc/free?#
Show solution
new allocates memory and calls the constructor, returns a typed pointer, and throws std::bad_alloc on failure; delete calls the destructor and frees the memory. malloc/free only allocate and free raw bytes, return void*, and return nullptr on failure. Never mix them (free on memory from new). In modern C++, use neither directly; use containers and smart pointers.
The standard library#
11. map vs unordered_map: when would you use each?#
Show solution
Use unordered_map for fast lookups when order does not matter; use map when you need sorted iteration or range queries (lower_bound).
12. How does std::vector grow, and what is the cost of push_back?#
Show solution
A vector stores elements contiguously with some spare capacity. When push_back exceeds the capacity, it allocates a larger block (typically 1.5× or 2× the size), moves or copies the elements, and frees the old block. That occasional reallocation is O(n), but it happens rarely enough that push_back is amortized O(1). Use reserve(n) when the final size is known.
#include <iostream>
#include <vector>
int main() {
std::vector<int> v;
v.reserve(1000);
std::size_t before = v.capacity();
for (int i = 0; i < 1000; ++i) v.push_back(i);
std::cout << std::boolalpha << (v.capacity() == before) << '\n';
}true13. What is iterator invalidation?#
Show solution
Some container operations make existing iterators, pointers and references to elements invalid. For a std::vector, any insertion that reallocates invalidates all of them, and insert/erase invalidates those at or after the position. Using an invalidated iterator is undefined behaviour. Classic bug: erasing inside a loop and then incrementing the erased iterator; the fix is it = v.erase(it); (or std::erase_if).
14. What happens when an exception is thrown?#
Show solution
The current function stops, and the runtime searches up the call stack for a matching catch. While unwinding, every local object is destroyed (destructors run), which is why RAII prevents leaks. If no handler matches, std::terminate is called. Throw by value, catch by const reference, and never let exceptions escape destructors.
Short coding questions#
15. Count the frequency of each word in a sentence.#
Show solution
#include <iostream>
#include <map>
#include <sstream>
#include <string>
int main() {
std::istringstream in("to be or not to be");
std::map<std::string, int> freq;
std::string word;
while (in >> word) ++freq[word];
for (const auto& [w, n] : freq) std::cout << w << ':' << n << ' ';
std::cout << '\n';
}be:2 not:1 or:1 to:2O(n log k) with std::map (k distinct words); O(n) on average with std::unordered_map, at the cost of unsorted output.
16. Implement a class with a static counter of live objects.#
Show solution
#include <iostream>
class Tracked {
public:
Tracked() { ++alive_; }
Tracked(const Tracked&) { ++alive_; }
~Tracked() { --alive_; }
static int alive() { return alive_; }
private:
static inline int alive_ = 0;
};
int main() {
Tracked a;
{
Tracked b;
Tracked c = b;
std::cout << Tracked::alive() << '\n';
}
std::cout << Tracked::alive() << '\n';
}3
1The copy constructor must also increment the counter; forgetting it is the bug interviewers look for.
Next steps#
Continue with the Advanced questions, or practise in Coding Challenges.
Finished this lesson?