Master your next C++ interview with our comprehensive collection of questions and expert-crafted answers. Get prepared with real scenarios that top companies ask.
Prepare for your C++ interview with proven strategies, practice questions, and personalized feedback from industry experts who've been in your shoes.
Thousands of mentors available
Flexible program structures
Free trial
Personal chats
1-on-1 calls
97% satisfaction rate
Choose your preferred way to study these interview questions
Memory leaks happen when allocated memory is never released, so the program loses the last way to free it. In C++, the usual causes are manual new or malloc without matching delete or free, ownership confusion across functions or classes, exception paths that skip cleanup, reference cycles with std::shared_ptr, and containers holding pointers longer than intended.
To detect them, I usually start with sanitizers like AddressSanitizer or LeakSanitizer, then use Valgrind on Linux for deeper leak reports. In production, rising memory over time is a clue. Prevention is mostly about design:
- Prefer RAII, objects clean themselves up in destructors
- Use std::unique_ptr by default, std::shared_ptr only for shared ownership
- Avoid raw owning pointers
- Keep ownership explicit in APIs
- Use reviews, tests, and sanitizers in CI to catch leaks early
I’ve used C++ mostly for performance-critical backend and systems work, where control over memory, latency, and concurrency really matters. My strongest experience is with modern C++, especially C++14/17, and writing code that is efficient but still maintainable.
std::thread, mutexes, atomics, and thread-safe queue patterns.What I usually emphasize is balancing performance with readability, because in production C++ that tradeoff matters a lot.
C is a procedural systems language. C++ keeps that low-level control, but adds abstraction and stronger type modeling, so the biggest differences show up in how you design and maintain code, not just syntax.
malloc/free; C++ prefers automatic lifetime and containers like std::vector, reducing leaks.Try your first call for free with every mentor you're meeting. Cancel anytime, no questions asked.
RAII means, “tie resource lifetime to object lifetime.” A resource can be memory, a file handle, a mutex, a socket, anything that must be released. In C++, you acquire it in a constructor and release it in the destructor, so cleanup happens automatically when the object goes out of scope, even if an exception is thrown.
std::lock_guard locks a mutex on construction, unlocks on destruction.std::unique_ptr, it owns heap memory and deletes it automatically.new/delete or paired open/close calls.I’d tell a junior dev: if something needs cleanup, make an object own it.
They improve correctness, readability, and optimization opportunities.
const makes intent explicit, readers immediately know what should not change.const T& avoids copies while guaranteeing no modification.const member functions separate observers from mutators cleanly.Immutability goes a step further. If objects do not change after construction, reasoning about state becomes much easier, especially with concurrency. Fewer writable states usually means fewer edge cases, less defensive code, and simpler testing. In practice, I treat const as the default and only allow mutation where it is clearly necessary.
These are C++ guidelines for resource-managing types.
std::string, std::vector, std::unique_ptr, and RAII wrappers handle cleanup.In practice, Rule of Zero drives most of my design. I prefer composition with standard library types, so I usually write no special member functions at all. If a class truly owns a low-level resource, I make ownership explicit, often non-copyable with std::unique_ptr, or I implement all five carefully and define clear copy and move semantics.
In C++, stack allocation means objects have automatic storage, like int x; or MyType obj;. They’re created when scope is entered and destroyed automatically when scope ends. Heap allocation means dynamic storage, usually via new, make_unique, or make_shared, and lifetime is controlled explicitly or through smart pointers.
In modern C++, if you need heap allocation, prefer std::unique_ptr or std::shared_ptr over raw new and delete.
They all let you work with an object indirectly, but they signal very different ownership and lifetime intent.
T*, can be null, can be reassigned, and may or may not own the object. Use it for optional access, C APIs, arrays, or when ownership is handled elsewhere.T&, is an alias to an existing object. It must refer to something valid and usually cannot be reseated. Use it for function parameters when null is not allowed.std::unique_ptr means exclusive ownership, cheapest and most common owning pointer.std::shared_ptr means shared ownership, use only when multiple owners are truly needed. std::weak_ptr breaks cycles and observes without owning.My rule of thumb: use references for non-owning required access, raw pointers for non-owning optional access, and smart pointers for ownership. Prefer unique_ptr by default.
Get personalized mentor recommendations based on your goals and experience level
Start matchingThey differ mainly in ownership.
std::unique_ptr has exclusive ownership, one pointer owns the object, cheap, deterministic, not copyable, movable.std::shared_ptr has shared ownership, object is destroyed when the last owner goes away, but it adds reference counting overhead.std::weak_ptr is a non-owning observer of a shared_ptr object, it does not keep the object alive, and you call lock() to get a temporary shared_ptr.Common misuse problems:
shared_ptr everywhere can hide ownership design and hurt performance.shared_ptr, like parent and child owning each other, cause leaks because ref counts never reach zero.weak_ptr without checking lock() can fail because the object may already be gone.shared_ptrs from the same raw pointer causes double delete.unique_ptr owner can create dangling references if lifetime is unclear.Object lifetime is the period from when storage is initialized as an object to when its destructor starts, or the storage is reused. In practice, think about how the object is created, who owns it, and when cleanup happens.
main and destroyed after it ends, though order across translation units can be tricky.new, live until delete; in modern C++, prefer RAII and smart pointers.const T&, or in C++11+, to T&& in some contexts.Think of it as ownership and lifetimes. An lvalue has a stable identity, you can take its address and assign to it, like a named variable. An rvalue is usually a temporary, like std::string("hi") or a function return you do not keep. An rvalue reference, T&&, lets you bind to that temporary and safely steal its resources instead of copying them.
T& usually means "this object persists", T&& usually means "this object can be moved from"vec.push_back(std::move(bigObj)) avoids expensive deep copiesmember = std::move(arg) inside the constructorCopy elision is when the compiler skips creating a temporary and constructs the object directly in its final destination. In modern C++, this matters a lot for return-by-value and temporary objects. Since C++17, some cases are guaranteed, like return T(...), so no copy or move happens at all.
std::vector, move is typically O(1), while copy is O(n).So move semantics improve the non-elided path, while copy elision removes the path entirely.
Pure virtual functions are virtual functions declared with = 0, like virtual void draw() = 0;. They say, "derived classes must implement this." A class with at least one pure virtual function is an abstract class, which means you cannot instantiate it directly.
I use them to define interfaces and enforce a contract across implementations:
- Example: IShape with draw() and area(), then Circle and Rectangle implement them.
- This lets client code work with IShape* or std::unique_ptr<IShape> without caring about concrete types.
- It improves extensibility, because new implementations plug in without changing callers.
- In production C++, I usually give the interface a virtual destructor too, virtual ~IShape() = default;.
- I’ve used this pattern for logger backends, storage providers, and hardware abstraction layers.
Shallow copy copies the member values as-is. If a class holds a raw pointer, both objects end up pointing to the same heap memory. Deep copy duplicates the pointed-to resource too, so each object owns its own separate copy.
int, double, std::array.delete, use-after-free, dangling pointers, and accidental shared state.char* data; destroying one frees data, the other now holds garbage.std::string, std::vector, std::unique_ptr, or implement copy constructor and copy assignment correctly, rule of three/five.If ownership is unclear, copies become a time bomb. Modern C++ avoids most of this by not manually owning memory unless necessary.
I default to composition, and use inheritance when I truly need substitutability.
is-a relationship, where derived objects must work anywhere the base is expected, following Liskov substitution.Shape with virtual draw(), and Circle, Rectangle implementations.has-a or uses-a relationship, like Car containing an Engine or a Logger.So, inheritance for stable abstractions and polymorphic APIs, composition for reuse and evolving designs.
Object slicing happens when you copy a derived object into a base object by value. The base part gets copied, but the derived-specific fields and behavior are sliced off. For example, if Derived inherits Base, then Base b = derived; loses the Derived part. Also, virtual dispatch will not save you if the object itself was sliced.
To avoid it:
- Prefer references or pointers, like Base& or Base*, when working polymorphically.
- Use smart pointers such as std::unique_ptr<Base> or std::shared_ptr<Base> for ownership.
- Avoid pass-by-value for base classes in APIs, use const Base& instead.
- If copying polymorphic objects is needed, use a virtual clone() that returns std::unique_ptr<Base>.
- Consider deleting base copy operations if value-copying would be dangerous.
Virtual functions let you call behavior through a base class interface and still get the derived class implementation at runtime. That is the core of runtime polymorphism in C++.
virtual, then overriding it in a derived class makes calls resolve by the object’s dynamic type, not the pointer or reference type.Base* p = new Derived; p->f(); calls Derived::f() if f is virtual.vptr.vptr points to a virtual table, or vtable, which stores function addresses for that class’s virtual functions.vptr, looks up the right slot in the vtable, and jumps to that function.It costs a small indirection, but enables flexible interfaces. Constructors do not dispatch to more-derived overrides.
Knowing the questions is just the start. Work with experienced professionals who can help you perfect your answers, improve your presentation, and boost your confidence.
Comprehensive support to help you succeed at every stage of your interview journey
We've already delivered 1-on-1 mentorship to thousands of students, professionals, managers and executives. Even better, they've left an average rating of 4.9 out of 5 for our mentors.
Find C++ Interview Coaches