Find and Fix C++ Ownership Bugs: Double Free, Shallow Copy, and Object Slicing
You are given C++ code that compiles and appears to work in simple cases but contains subtle bugs related to resource management and object semantics. Your task is to identify these bugs, explain their root causes, and provide corrected C++ code.
Scenario A: Resource Ownership (Buffer class)
A Buffer class manages a dynamically allocated array of integers. It relies on the compiler-generated copy constructor and copy assignment operator.
class Buffer {
public:
explicit Buffer(std::size_t n) : size_(n), data_(new int[n]) {}
~Buffer() { delete[] data_; } // Potential double free
int& at(std::size_t i) { return data_[i]; } // Assumes valid data_
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 - shallow copy
Buffer c(5);
c = a; // (2) copy-assign a into c - shallow copy
} // (3) a, b, c all destruct here - double free on data_
Identify the issues with the copy constructor and copy assignment operator, explain why they lead to double frees and memory leaks, and provide a corrected Buffer class that implements proper value semantics (deep copying).
Scenario B: Polymorphism by Value (Shape hierarchy)
A base class Shape and a derived class Circle are intended to be stored in a std::vector<Shape>. When a Circle object is added to the vector, it is copied by value.
struct Shape {
virtual double area() const { return 0.0; }
// Missing virtual destructor
};
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...
} // objects in vector are destroyed here
Identify the problem that occurs when storing derived objects by value in a container of base objects, specifically concerning object slicing and the implications for polymorphism and destruction. Provide a corrected approach to handle polymorphic types in a container.
Constraints & Assumptions:
- C++17, single-threaded.
Bufferis 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.
AI interview prep
Core dilemma
Balancing correct resource management (preventing leaks and double frees) with maintaining proper object semantics (value vs. identity, polymorphism) in C++.
3 tradeoffs and 3 stress tests locked
Interview prep
Unlock the interviewer X-Ray rubric, tradeoff matrix, stress tests, clarifying questions, and key concepts.
Find and Fix C++ Ownership Bugs: Double Free, Shallow Copy, and Object Slicing
You are given C++ code that compiles and appears to work in simple cases but contains subtle bugs related to resource management and object semantics. Your task is to identify these bugs, explain their root causes, and provide corrected C++ code.
Scenario A: Resource Ownership (Buffer class)
A Buffer class manages a dynamically allocated array of integers. It relies on the compiler-generated copy constructor and copy assignment operator.
class Buffer {
public:
explicit Buffer(std::size_t n) : size_(n), data_(new int[n]) {}
~Buffer() { delete[] data_; } // Potential double free
int& at(std::size_t i) { return data_[i]; } // Assumes valid data_
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 - shallow copy
Buffer c(5);
c = a; // (2) copy-assign a into c - shallow copy
} // (3) a, b, c all destruct here - double free on data_
Identify the issues with the copy constructor and copy assignment operator, explain why they lead to double frees and memory leaks, and provide a corrected Buffer class that implements proper value semantics (deep copying).
Scenario B: Polymorphism by Value (Shape hierarchy)
A base class Shape and a derived class Circle are intended to be stored in a std::vector<Shape>. When a Circle object is added to the vector, it is copied by value.
struct Shape {
virtual double area() const { return 0.0; }
// Missing virtual destructor
};
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...
} // objects in vector are destroyed here
Identify the problem that occurs when storing derived objects by value in a container of base objects, specifically concerning object slicing and the implications for polymorphism and destruction. Provide a corrected approach to handle polymorphic types in a container.
Constraints & Assumptions:
- C++17, single-threaded.
Bufferis 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.