116. Deep Copy Assignment Buffer

You are developing a firmware-style C++ class that stores sensor readings inside a dynamically allocated byte buffer.
Each buffer instance must own its memory exclusively and follow correct ownership and lifetime rules.

Your task is to implement the copy assignment operator so that assigning one object to another using:

A = B; 

performs a deep copy.

A deep copy means:

  • The destination object allocates its own buffer
  • All bytes from the source buffer are fully duplicated
  • No memory is shared between objects

If implemented incorrectly, the program may suffer from:

  • Data corruption due to shared memory
  • Double-free errors
  • Hard-to-diagnose crashes common in embedded systems

 

What the class must support

The SensorBuffer class must implement:

  • A constructor that dynamically allocates a byte buffer of a specified size
  • A destructor that correctly frees the allocated memory
  • A copy assignment operator that:
    • Detects self-assignment
    • Releases any previously owned memory
    • Allocates a new buffer matching the source object’s size
    • Copies every byte from the source buffer

The class also provides helper functions to:

  • Modify individual buffer elements
  • Print the contents of the buffer

 

Program Flow

  1. Read an integer N1 followed by N1 byte values → initialize object A
  2. Read an integer N2 followed by N2 byte values → initialize object B
  3. Perform the assignment A = B
  4. Print the final contents of object A

After the assignment:

  • A must have the same number of elements as B
  • A must contain the same values as B
  • A must own an independent memory buffer

 

Example Input

5
1 2 3 4 5
5
9 9 9 9 9

Example Output

9 9 9 9 9 

 

Constraints

  • Buffer size ranges from 1 to 100
  • Each buffer value is in the range 0–255
  • All buffers must be dynamically allocated using new[]
  • All allocated memory must be released using delete[]
  • The copy assignment must perform a deep copy
  • Output must be space-separated with no trailing spaces

 

 

 

Need Help? Refer to the Quick Guide below

In C++, when you assign one object to another (a = b) or pass by value, the compiler performs a Shallow Copy by default. It copies the bits of the object exactly.

  • Safe for simple types (int, float, GPIO_Config).
  • Dangerous for objects managing pointers or resources. If b holds a pointer to a buffer, a gets a copy of the pointer, not the buffer. Both objects now point to the same memory. When one dies, it frees the memory, leaving the other with a Dangling Pointer.

To fix this, we implement Deep Copy logic using the Copy Constructor and Copy Assignment Operator.

Syntax & Usage

1. Copy Constructor

Used when creating a new object from an existing one (Buffer b2 = b1;).

class Buffer {
    int* ptr;
    int size;
public:
    // 1. Normal Constructor
    Buffer(int s) : size(s) { ptr = new int[size]; }

    // 2. Copy Constructor (Deep Copy)
    Buffer(const Buffer& other) : size(other.size) {
        ptr = new int[size]; // Allocate NEW memory
        memcpy(ptr, other.ptr, size * sizeof(int)); // Copy data
    }
    
    ~Buffer() { delete[] ptr; }
};

2. Copy Assignment Operator

Used when updating an already existing object (b2 = b1;). This is more complex because b2 already has memory that must be cleaned up first.

    // 3. Copy Assignment Operator
    Buffer& operator=(const Buffer& other) {
        if (this == &other) return *this; // Handle self-assignment (b1 = b1)

        delete[] ptr;        // Free old memory
        
        size = other.size;   // Copy size
        ptr = new int[size]; // Allocate NEW memory
        memcpy(ptr, other.ptr, size * sizeof(int)); // Copy data
        
        return *this;        // Return reference for chaining (a = b = c)
    }

Deep vs. Shallow Copy Visualization

FeatureShallow Copy (Default)Deep Copy (Custom)
PointersCopies the address (pointer value).Copies the data at the address.
MemoryShared between objects (Risky).Independent memory per object.
DestructionDouble-Free crash (Both free same ptr).Safe (Each frees its own).
SpeedFast (copying 4 bytes).Slow (copying N bytes).

Relevance in Embedded/Firmware

1. The "Rule of Three"

If your class needs a custom Destructor (to free memory or close a handle), you must also define a Copy Constructor and Copy Assignment Operator.

In firmware, this often applies to circular buffers, packet managers, or flash file system wrappers.

2. Disabling Copy (Hardware Ownership)

For hardware drivers (e.g., UART, SPI), copying makes no sense. You cannot "clone" a physical peripheral.

The standard practice is to delete copy semantics to enforce Unique Ownership.

class UART {
public:
    UART(const UART&) = delete;            // ❌ No Copying
    UART& operator=(const UART&) = delete; // ❌ No Assignment
};

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Double FreeThe #1 bug with default copy. Object A is destroyed (frees ptr). Object B is destroyed (frees same ptr) → System Hard Fault.
❌ Self-AssignmentIf you write obj = obj;, a naive assignment operator might delete ptr before copying from it, corrupting data. Always check if (this == &other).
❌ Object SlicingIf you assign a Derived object to a Base object variable (Base b = Derived d;), the "Derived" parts are sliced off (lost). Always pass polymorphic objects by pointer or reference.
✅ Pass by ReferenceTo avoid the performance cost of Deep Copies, always pass objects to functions using const Reference (void func(const Buffer& b)), not by value.