Katkat

Find and Fix C++ Ownership Bugs: Double Free, Shallow Copy, and Object Slicing

SIG (Susquehanna)Software Engineer · Onsite

Last updated

Tests a candidate's ability to identify and fix critical C++ resource management bugs, specifically shallow copies leading to double-free errors and object slicing caused by improper pass-by-value semantics with polymorphic types.

Scenario

You are reviewing a C++ order processing module written for an internal messaging component. The snippet below manages dynamic memory and uses inheritance for specialized order buffers, but it exhibits serious ownership and polymorphism bugs.

#include 
#include 

class OrderBuffer {
public:
    OrderBuffer(size_t size) : size_(size), data_(new char[size]) {
        std::memset(data_, 0, size_);
    }

    virtual ~OrderBuffer() {
        delete[] data_;
    }

    virtual void process() const {
        std::cout << "Processing standard buffer of size " << size_ << "\n";
    }

protected:
    size_t size_;
    char* data_;
};

class PriorityOrderBuffer : public OrderBuffer {
public:
    PriorityOrderBuffer(size_t size, int priority)
        : OrderBuffer(size), priority_(priority) {}

    void process() const override {
        std::cout << "Processing priority buffer (priority: " 
                  << priority_ << ") of size " << size_ << "\n";
    }

private:
    int priority_;
};

void handleBuffer(OrderBuffer buf) {
    buf.process();
}

int main() {
    PriorityOrderBuffer pbuf(256, 1);
    handleBuffer(pbuf);
    return 0;
}

Tasks

  1. Identify the Bugs: Pinpoint and explain why the code causes undefined behavior (specifically, a double-free) and object slicing.
  2. Explain the Root Cause: Discuss the Rule of Three/Five violation in OrderBuffer, and explain what occurs during the invocation of handleBuffer(pbuf).
  3. Refactor the Code:
    • Provide a corrected version using modern C++ principles (such as RAII, the Rule of Zero, and smart pointers or std::vector).
    • Fix the function signature and invocation to properly support polymorphic dispatch without slicing or unnecessary allocations.

Hints

Hint 1

Look closely at what happens to the raw pointer member when the compiler synthesizes the copy constructor for OrderBuffer.

Hint 2

Notice how handleBuffer accepts its parameter by value rather than by reference or pointer.

Hint 3

Consider how applying the Rule of Zero using standard library containers or unique ownership eliminates manual delete calls entirely.

Follow-up questions

  • How does the copy-and-swap idiom help implement strong exception safety if you must manually manage resources in C++?
  • In a high-frequency trading context where dynamic memory allocation on the critical path is prohibited, how would you redesign these buffers to avoid heap allocations entirely?
  • What are the performance implications of virtual dispatch (vtable lookups) and cache misses when passing polymorphic objects via pointers or references versus using compile-time polymorphism (e.g., CRTP or `std::variant`)?