C++RAIILifetime

C++ Backend Interview Questions

C++ backend interviews are lifetime interviews. Nearly every serious question reduces to who owns this object, when does it die, and what happens if something touches it afterwards. Dangling references, use-after-move, iterator invalidation and double ownership are all the same question asked from different angles.

The second theme is that correct-looking code can be undefined. A test passing is weaker evidence in C++ than in most languages, because undefined behaviour frequently does what you expected until a compiler upgrade or an optimisation level changes its mind. Interviewers probe whether you know which constructs are actually undefined, because that knowledge is what separates safe C++ from lucky C++.

Written and reviewed by Sahil Srivastav

What the bar actually is

You should express ownership in types rather than in comments: `unique_ptr` for sole ownership, `shared_ptr` only where ownership is genuinely shared, references and raw pointers for non-owning access with a lifetime you can justify. A candidate who reaches for `shared_ptr` everywhere is signalling that they have not thought about lifetime at all.

You are also expected to know RAII as the mechanism that makes exception safety possible, not as a slogan. Every resource — memory, file descriptors, locks, connections — acquired in a constructor and released in a destructor means every exit path, including a thrown exception, releases correctly. This is the question behind most "is this code safe" prompts.

How the rounds are structured

Machine coding with ownership pressure

A small system where objects outlive or must not outlive each other. Expect follow-ups on what happens when the container reallocates or an element is removed.

Lifetime and ownership questions

Smart pointers, move semantics, iterator invalidation, dangling references. The core round for backend C++ roles.

Concurrency and the memory model

Data races as undefined behaviour, `std::atomic` and memory ordering at a working level, why a non-atomic flag is not merely slow but wrong.

Performance reasoning

Allocation in hot paths, cache behaviour, copies you did not intend. Usually probed through a concrete snippet rather than in the abstract.

What this interview bar tests

Ownership encoded in the type

`unique_ptr` by default; `shared_ptr` when lifetime is genuinely shared and you accept the atomic refcount cost; non-owning raw pointer or reference only where the lifetime is provably longer.

RAII and exception safety

Acquisition in a constructor and release in a destructor is what makes every exit path safe. Manual release before a `return` is the pattern that leaks the moment something throws.

Undefined behaviour you can name

Use-after-move, dereferencing an invalidated iterator, data races, signed overflow. Knowing these are undefined rather than merely risky is the probed distinction.

Move semantics in practice

What a moved-from object is valid for, why `std::move` is a cast rather than an action, and when a copy you did not write is costing you.

A preparation plan that works

  1. 1Take a class that manages a resource manually and convert it to RAII, then throw an exception in the middle and confirm nothing leaks. That exercise is the heart of the ownership round.
  2. 2Write the iterator-invalidation bug deliberately: hold an iterator across a `push_back` that reallocates. Then run it under a sanitizer so you see it reported rather than getting away with it.
  3. 3Build the same small structure with `unique_ptr` and with `shared_ptr` and articulate what the second buys and costs. Being able to justify the choice is the signal.
  4. 4Compile with `-fsanitize=address,undefined` on a concurrency exercise. Candidates who mention sanitizers unprompted are immediately credible.

Questions you should expect

When would you use `shared_ptr` over `unique_ptr`?

Only when ownership is genuinely shared and the last owner is not knowable at compile time — a cache handed out to multiple consumers, for instance. It costs an atomic refcount on every copy and introduces cycle risk requiring `weak_ptr`. Defaulting to `shared_ptr` is read as not having reasoned about lifetime.

What is the state of an object after it is moved from?

Valid but unspecified. You may destroy it or assign to it; you may not assume anything about its contents. Reading a moved-from object is a bug even when it appears to work, which is exactly the class of defect that survives testing and fails after a compiler change.

Why is a data race undefined behaviour rather than just a wrong answer?

Because the standard gives the compiler licence to assume races do not occur, so it may reorder, cache in a register, or eliminate code based on that assumption. The result is not a torn value you can reason about — it is a program whose behaviour no longer corresponds to the source. That is why `std::atomic` is a correctness tool, not an optimisation.

This function takes a `const std::string&` and stores it. What is wrong?

Storing a reference to a caller-owned temporary leaves a dangling reference once the temporary dies. Either take by value and move, or take ownership explicitly. This is the single most common lifetime bug in backend C++ code.

Where does the allocation cost come from in this hot path?

Usually from copies nobody intended — a container passed by value, a string built per iteration, a temporary returned and copied rather than moved — plus per-element allocation where a reserve would have done. Name the fix in terms of allocations per operation, which is the number the interviewer cares about.

What gets candidates rejected

  • Using `shared_ptr` by default, which signals no lifetime reasoning
  • Manual resource release before a `return`, which leaks on any thrown exception
  • Treating a data race as a performance issue rather than undefined behaviour
  • Storing references to caller-owned temporaries
  • Holding iterators across operations that may reallocate
  • Reading a moved-from object because it happens to work
  • Never having used a sanitizer, then claiming the code is correct because tests pass

What to practise, in order

Lock leaked on an exception path

The RAII lesson as an executable problem: a lock acquired and never released when something throws, with tests that assert progress afterwards.

Allocation rate explosion in a hot path

Churn that pins the runtime while leaking nothing. Tests assert allocations per operation rather than wall-clock time.

Coarse lock convoy in a shared registry

Contention that is not a deadlock and does not show up as an error — just collapsing throughput under load.

Inventory overselling under concurrency

Check-then-write under parallel access, with the invariant asserted rather than the happy path.

Practise in a real repository

Gronex ships broken backend repositories with failing test suites that encode the production invariant. You read the evidence, find the defect, and make the tests pass — which is what the round actually measures, rather than whether you can recite a definition.

FAQ

Which C++ standard should I target?

Write modern C++ — C++17 idioms are a safe baseline, C++20 if the role mentions it — and be honest about what you have shipped. Claiming familiarity with features you have only read about is risky because follow-ups tend to be specific about behaviour.

Is C++ common for backend roles in India?

In specific domains: trading and market data, systems and infrastructure, game backends, and performance-critical services. Those interviews weight lifetime, allocation and latency far more heavily than general backend loops do.

How much template metaprogramming is tested?

Much less than its reputation suggests. Interviewers mostly care that you can use templates to remove duplication and know when they make code unreadable. Deep metaprogramming is a specialist topic, not a backend screening topic.

Do I need to know memory ordering in detail?

Know that the default `seq_cst` is correct and that relaxed orderings are an optimisation requiring proof. Being able to say "I would use the default unless I could demonstrate the weaker ordering is safe" is a better answer than a half-remembered acquire-release explanation.

Related

Other preparation tracks