Find and Fix C++ Ownership Bugs: Double Free, Shallow Copy, and Object Slicing
Company: Sig
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Onsite
You are given C++ code that compiles and "works" in simple cases but is subtly broken. Read it, identify every bug, explain the underlying root cause, and rewrite it correctly.
**Scenario A — a resource-owning class.** `Buffer` owns a heap allocation through a raw pointer and relies on the compiler to generate its copy operations:
```cpp
class Buffer {
public:
explicit Buffer(std::size_t n) : size_(n), data_(new int[n]) {}
~Buffer() { delete[] data_; }
int& at(std::size_t i) { return data_[i]; }
std::size_t size() const { return size_; }
private:
std::size_t size_;
int* data_;
};
void use_a() {
Buffer a(10);
Buffer b = a; // (1) copy-construct b from a
Buffer c(5);
c = a; // (2) copy-assign a into c
} // (3) a, b, c all destruct here
```
**Scenario B — a polymorphic type stored by value.** `Shape` is a base class; concrete shapes derive from it. They are stored and copied through a base-typed container:
```cpp
struct Shape {
virtual double area() const { return 0.0; }
};
struct Circle : Shape {
double r;
explicit Circle(double r) : r(r) {}
double area() const override { return 3.141592653589793 * r * r; }
};
void use_b(std::vector<Shape>& shapes) {
shapes.push_back(Circle(2.0)); // store a circle
double a = shapes.back().area(); // expect ~12.566...
}
```
For each scenario, state exactly what goes wrong, why, and provide a corrected version.
```hint What to look at first
For Scenario A, count how many times each `new[]` allocation gets passed to `delete[]`. If two `Buffer` objects end up holding the same `data_` pointer, whose destructor runs on it — and how many times?
```
```hint Which functions did the author rely on?
The author wrote a destructor but no copy constructor or copy assignment operator. For a class that owns a raw pointer, are the compiler-generated defaults correct? (Rule of Three / Rule of Five.)
```
```hint Polymorphism by value
In Scenario B, `std::vector<Shape>` stores `Shape` objects by value. What happens to the `Circle`-specific part of the object when it is copied into a `Shape`-sized slot? What do a virtual `clone()` and pointer/`unique_ptr` storage buy you?
```
### Constraints & Assumptions
- C++17, single-threaded.
- `Buffer` is intended to have value semantics: copyable, with each copy independently owning its own buffer.
- The polymorphic shapes are meant to be stored and later queried without losing their dynamic type.
- Prefer standard idioms (RAII, Rule of Five, smart pointers) over manual reference counting.
### Clarifying Questions to Ask
- Should `Buffer` be deep-copyable, move-only, or non-copyable? (That decides which special members to write or `=delete`.)
- Is exception safety (a strong guarantee on assignment) in scope, or only ownership correctness?
- For the shapes, do we own the objects (so we must clone and delete them) or merely reference objects owned elsewhere?
- May I use `std::unique_ptr`/`std::shared_ptr`, or should the fix be raw-pointer-only to demonstrate the Rule of Five explicitly?
### What a Strong Answer Covers
```premium-lock What a Strong Answer Covers
```
### Follow-up Questions
- How does copy-and-swap deliver the strong exception guarantee, and what does it require of `swap` and the move constructor?
- When would you make `Buffer` move-only (`=delete` the copies) instead of deep-copyable, and how would call sites change?
- Why is deleting a derived object through a base pointer with a non-virtual destructor undefined behavior?
- How would a `std::variant<Circle, Square, ...>` or another closed sum type compare to the `clone()`-based open polymorphism for storing heterogeneous shapes?
Quick Answer: This question evaluates a candidate's understanding of C++ object lifetime, copy semantics, and polymorphism. It tests the ability to spot shallow-copy bugs like double free and object slicing, and to reason about the Rule of Three/Five, commonly asked to assess practical memory-management and design skill in software engineering interviews.