C++ Interview Questions: Practical and Conceptual, With Spot-the-Bug Exercises

Published · by JOBB GROUP FZE

C++ interviews are rarely about obscure syntax. They come back, again and again, to one question in different clothes: who owns this memory, and what happens when things go wrong? Ownership, object lifetime, copying versus moving, polymorphism and undefined behaviour account for most of the conceptual questions. The practical round then checks whether you can apply those ideas to a short piece of code.

This guide is organised by concept. Each section explains the idea, lists the questions it usually generates, and ends with a short “spot the bug” exercise of the kind interviewers put on a whiteboard or screen. Code is illustrative; the explanations are checked against Microsoft’s C++ documentation and cppreference.

Concept 1: RAII and ownership

Resource Acquisition Is Initialisation is the idea that ties C++ together. Microsoft’s documentation states its main principle as giving ownership of any heap-allocated resource to a stack-allocated object whose destructor contains the code to free it. Because destructors run when an object goes out of scope, including during stack unwinding after an exception, the resource is always released.

RAII is not only about memory. File handles, sockets, mutex locks and database transactions all follow the same pattern: std::lock_guard releases a mutex when it goes out of scope, and std::fstream closes its file.

Questions it generates

  • What is RAII, and why does it make code exception-safe?
  • Why should you avoid raw new and delete in modern C++?
  • How would you wrap a C library handle (for example a FILE*) safely?

Spot the bug

void process(const std::string& path) {
    Buffer* buf = new Buffer(1024);
    parse(path, *buf);   // may throw
    delete buf;
}

Answer: if parse throws, delete never runs and the buffer leaks. Use auto buf = std::make_unique<Buffer>(1024); or simply a stack object, and the destructor handles clean-up on every path.

Concept 2: Smart pointers

The standard library offers three, and interviewers expect you to choose between them, not just list them. Microsoft’s guide summarises:

Type Ownership Key facts Use when
std::unique_ptr Exactly one owner Can be moved but not copied; the size of one pointer The default choice
std::shared_ptr Shared, reference-counted The object is deleted when the last owner releases it; holds a pointer plus a pointer to a control block Ownership is genuinely shared and lifetimes cannot be predicted
std::weak_ptr None (observer) Does not take part in reference counting Observing a shared object, or breaking a shared_ptr cycle

Microsoft’s guidance is to use unique_ptr as the default unless you know for certain you need shared_ptr. In practice, std::make_unique (C++14) and std::make_shared are the idiomatic ways to create them; make_shared can allocate the object and its control block together.

Questions it generates

  • When would you pick shared_ptr over unique_ptr? (A strong answer: rarely, and with a specific reason.)
  • How do you transfer ownership of a unique_ptr to a function? (By value, with std::move at the call site.)
  • What is a reference cycle and how do you break it?
  • Should a function that only reads an object take a smart pointer? (Usually no: take a reference or raw pointer, and leave ownership with the caller.)

Spot the bug

struct Node {
    std::shared_ptr<Node> parent;
    std::vector<std::shared_ptr<Node>> children;
};

Answer: parent and child each hold a shared_ptr to the other, so neither count reaches zero and the tree is never freed. Make parent a std::weak_ptr<Node> (or a raw non-owning pointer if the parent always outlives the child).

Concept 3: Copying, moving and the special member functions

A class has five special members that control copying, moving and destruction: the destructor, copy constructor, copy assignment, move constructor and move assignment. cppreference summarises the guidelines:

  • Rule of three: if a class needs a user-defined destructor, copy constructor or copy assignment operator, it almost certainly needs all three.
  • Rule of five: a class for which move semantics are desirable has to declare all five.
  • Rule of zero: other classes should not declare any of them, and should instead hold members (smart pointers, containers) that already manage themselves.

Moving transfers resources instead of copying them. Microsoft’s example moves a heap array by taking the source’s pointer and length, then setting the source’s pointer to nullptr and length to zero, so the source’s destructor will not free the memory a second time. std::move does not move anything by itself: it casts its argument to an rvalue so that the move overload is chosen.

Two details that separate good answers. First, the move assignment operator should guard against self-assignment and release the object’s existing resource before taking the new one, as Microsoft’s example does. Second, marking move operations noexcept matters: when std::vector reallocates, it moves elements only if the move cannot throw (or the type cannot be copied at all); a copyable type with a potentially throwing move is copied instead, to keep the container’s exception guarantee. Microsoft’s own example declares its move constructor and move assignment noexcept.

Questions it generates

  • What state is an object in after it has been moved from? (Valid but unspecified: you may assign to it or destroy it, but should not rely on its value.)
  • Explain the difference between an lvalue and an rvalue.
  • Why is std::move on a const object usually a mistake? (A const rvalue cannot bind to a move constructor, so it copies.)

Spot the bug

class Buffer {
    char* data_;
public:
    explicit Buffer(size_t n) : data_(new char[n]) {}
    ~Buffer() { delete[] data_; }
};

Buffer a(100);
Buffer b = a;   // ?

Answer: the class defines a destructor but relies on the compiler-generated copy constructor, which copies the pointer. Both objects then delete[] the same memory. Either follow the rule of five (implement or delete copy and move operations) or, better, follow the rule of zero by storing std::vector<char> or std::unique_ptr<char[]>.

Concept 4: Polymorphism and virtual destructors

Virtual functions enable run-time dispatch through a base-class pointer or reference, typically implemented with a per-class table of function pointers (a vtable). The classic interview question follows immediately: why does a polymorphic base class need a virtual destructor?

The C++ Core Guidelines rule C.35, as reproduced in Microsoft’s code-analysis documentation, says a base class destructor should be either public and virtual, or protected and non-virtual. The reasoning: client code may destroy objects through a base pointer, so the destructor must dispatch to the real type to release the derived class’s resources. If clients should never delete through the base, make the destructor protected so the mistake will not compile.

Questions it generates

  • What is object slicing? (Copying a derived object into a base object by value drops the derived part.)
  • What do override and final do, and why use them?
  • Can you call a virtual function from a constructor? (You can, but it dispatches to the class currently being constructed, not the most derived one.)

Spot the bug

struct Shape { virtual double area() const = 0; };
struct Image : Shape {
    std::vector<Pixel> pixels;
    double area() const override;
};

std::unique_ptr<Shape> s = std::make_unique<Image>();

Answer: Shape has no virtual destructor, so destroying an Image through the unique_ptr<Shape> is undefined behaviour and the vector’s destructor may never run. Add virtual ~Shape() = default;.

Concept 5: Undefined behaviour

cppreference separates several kinds of behaviour, and interviewers like to probe the differences:

  • Ill-formed: the program has syntax or diagnosable semantic errors, and the compiler must issue a diagnostic.
  • Ill-formed, no diagnostic required: semantic errors that may not be diagnosable in general, so the compiler need not report them.
  • Implementation-defined: behaviour varies between implementations, and each must document it (for example, the size of int).
  • Unspecified: one of several valid outcomes, not necessarily documented (for example, evaluation order of function arguments).
  • Erroneous: a newer category for incorrect behaviour that implementations are recommended to diagnose, rather than leaving it fully undefined.
  • Undefined: there are no restrictions on what the program does.

Examples cppreference lists include data races, out-of-bounds array access, signed integer overflow, null pointer dereference and reading an uninitialised scalar. Its most important warning is that compilers may produce unexpected results when a program with undefined behaviour is compiled with optimisation enabled, because the optimiser assumes it never happens. That is why “it works in debug” is not evidence of correctness.

Spot the bug

bool will_overflow(int x) {
    return x + 1 < x;
}

Answer: signed overflow is undefined, so the compiler may assume x + 1 < x is always false and reduce the function to return false;. Compare against std::numeric_limits<int>::max() instead.

Other topics to revise

  • Containers and iterators: when std::vector invalidates iterators, why it is usually the default container, and when std::unordered_map beats std::map.
  • const correctness: const member functions, const references as parameters, and constexpr.
  • Templates: function and class templates, and for more senior roles, concepts (C++20) and SFINAE.
  • Concurrency: std::thread, std::mutex with std::lock_guard or std::scoped_lock, std::atomic, and what a data race is.
  • Build and tooling: the difference between compiling and linking, header guards, and how you would use sanitisers (AddressSanitizer, UndefinedBehaviorSanitizer) to find bugs.

Freshers and experienced candidates: what changes

Freshers are usually asked to explain pointers versus references, stack versus heap, classes and inheritance, the basics of the standard library, and to write a small function correctly. Showing you know RAII and unique_ptr already sets you apart from candidates who learned C++ as “C with classes”.

Experienced candidates should expect the spot-the-bug format, design discussions (how would you structure ownership in this system?), performance questions about cache-friendly data layout, and domain specifics: real-time constraints in embedded work, latency in trading systems, or engine architecture in games. Be ready with one story about a memory or concurrency bug you found and how.

How to practise

  1. Write each spot-the-bug example above, build it with warnings on (-Wall -Wextra) and with sanitisers, and observe what they report.
  2. Implement a small class that owns a resource, first with the rule of five, then refactor it to the rule of zero.
  3. Explain each concept aloud in under two minutes. Interviewers value clear explanations as much as correct code.

Make sure your CV names the C++ standard you work with, your toolchain and the kind of systems you built. The software engineer resume builder is a good starting point, and you can check the result with the ATS resume score checker.

Frequently asked questions

What C++ interview questions do freshers usually get?

Pointers and references, stack versus heap, constructors and destructors, inheritance and virtual functions, and basic standard library containers. Expect to write a short function and talk through it.

What do experienced C++ interviews focus on?

Ownership design, move semantics, undefined behaviour, concurrency, performance and debugging stories, often through code-review or spot-the-bug exercises.

Which C++ standard should I prepare for?

Check the job advert. Most modern roles expect fluency in C++11 and C++14 features such as smart pointers, move semantics and lambdas, and many use C++17 or C++20. Be clear about which standards you have used in production.

Is it acceptable to say I do not know an answer?

Yes. Say what you do know, reason towards an answer and say how you would confirm it, for example by checking cppreference or writing a small test. That is more convincing than guessing.

Sources

Ready to build your resume?

Start with a sample or a blank page. No sign-up needed. Free to edit; US$1.00 only if you download a PDF.