121. Move Constructor Buffer Transfer

In embedded firmware systems, sensor data buffers are frequently passed between tasks, queues, or ISR-to-thread boundaries. These buffers often own dynamically allocated memory, and copying them can be expensive on memory- and performance-constrained hardware.

To reduce overhead, such systems rely on move semantics, allowing ownership of resources to be transferred instead of copied.

You are given a SensorBuffer class that owns a dynamically allocated byte buffer. Your task is to implement a move constructor that transfers ownership of the buffer safely and efficiently.

After implementing the move constructor:

  • Ownership of the buffer must be transferred to the destination object
  • No deep copy of the buffer must occur
  • The source object must be left in a valid, empty state
  • No memory leak must occur
  • No double-free must occur when objects are destroyed

Program Flow:

  1. Read integer N
  2. Read N bytes
  3. Construct buffer A with size N
  4. Move-construct buffer B from A
  5. Print buffer A
  6. Print buffer B

Expected Behavior After Move:

  • Buffer A prints: No data
  • Buffer B prints the original byte sequence

Example Input:

5
10 20 30 40 50 

Example Output:

No data
10 20 30 40 50 

Constraints:

  • 1 ≤ N ≤ 100
  • Buffer memory must be allocated using new[]
  • The move constructor must transfer ownership only (no copying)
  • The source object must be safe to destroy after being moved from
  • If the buffer is empty, output must be exactly:

    No data 
  • Output formatting must match exactly, including spaces and newlines

 

 

 

 

Need Help? Refer to the Quick Guide below

In C++, copying an object (Deep Copy) is expensive—it involves allocating new memory and copying data byte-by-byte.

Move Semantics (introduced in C++11) allows you to transfer ownership of resources (like memory pointers or file handles) from one object to another without copying the actual data.

Think of it as "Stealing" the resources.

  • Copy: I photocopy your notes. Now we both have sheets of paper. (Slow)
  • Move: I take the notes from your hand. I have them, you have nothing. (Fast).

Syntax & Usage

1. R-value References (&&)

To distinguish between "Copyable" and "Movable" objects, C++ uses &&.

  • L-value (Type&): A persistent variable (x). You Copy it.
  • R-value (Type&&): A temporary object (Type()) or explicitly moved object. You Move it.

2. Move Constructor & Assignment

Instead of allocating new RAM, we just copy the pointer and set the old pointer to nullptr.

class Buffer {
    uint8_t* ptr;
public:
    // Move Constructor
    Buffer(Buffer&& other) noexcept {
        this->ptr = other.ptr;  // 1. Steal the pointer
        other.ptr = nullptr;    // 2. Nullify the source (Prevent double-free)
    }

    // Move Assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] ptr;           // Free my current memory
            this->ptr = other.ptr;  // Steal
            other.ptr = nullptr;    // Nullify
        }
        return *this;
    }
};

3. std::move

Forces a move on a named variable.

Buffer b1(100);
Buffer b2 = std::move(b1); // b2 owns the memory. b1 is now empty.

Copy vs. Move Visualization

FeatureCopy (const Type&)Move (Type&&)
OperationDuplicate Data (Deep Copy).Transfer Pointer (Shallow Copy).
Old ObjectRemains valid and unchanged.Valid but Empty (gutted).
CostHigh (alloc + memcpy).Near Zero (pointer assignment).
HardwareDuplicates logic (Bad for drivers).Transfers ownership (Good for drivers).

Relevance in Embedded/Firmware

1. High-Performance Returns

In C, to avoid copying a large struct, we pass pointers: void get_data(BigStruct* out).

In C++, Move Semantics allow you to return by value efficiently.

// Returns a heavy object (e.g., 1KB buffer)
Packet receive_packet() {
    Packet p;
    // ... fill p ...
    return p; // Compiler automatically Moves this out. Zero overhead.
}

2. Driver Ownership (std::unique_ptr)

Hardware drivers (UART, SPI) are "non-copyable" (you can't clone a physical peripheral). However, you often want to move a driver around (e.g., create it in init() and pass it to a Manager class).

Move semantics allow this safe transfer of ownership without cloning.

3. Container Performance

If you have a std::vector<String>, resizing the vector requires moving all elements to a new memory block.

  • Without Move: Every string is malloc'd and copied. (Slow, heap fragmentation).
  • With Move: Only pointers are swapped. (Fast, no heap churn).

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Use After Move

Accessing an object after moving from it is undefined logic (usually crashes).

auto b2 = std::move(b1); b1.send(); -> Crash (b1 is empty).

std::move doesn't movestd::move(x) generates no code. It effectively just casts x to x&&, telling the compiler "Please try to use the Move Constructor." If you didn't write a Move Constructor, it silently falls back to Copy.
noexceptAlways mark move constructors as noexcept. If a move can throw an exception, standard containers (like vector) will refuse to use it and will Copy instead.
✅ Rule of FiveIf you write a custom Destructor, you typically need: Copy Ctor, Copy Assign, Move Ctor, Move Assign.