# Copy and Move Semantics and the Rule of Zero, Three and Five — C++

Source: https://www.skillbyai.com/en/cpp/o-rule

> Understand copying, moving and which special members a class needs.

## What happens when objects are copied or moved

Every class has **special member functions** the compiler can generate: default constructor, **copy constructor**, **copy assignment**, **move constructor**, **move assignment** and **destructor**. Copying duplicates an object's state; **moving** (C++11) transfers resources from an object that is about to disappear, such as a temporary, leaving it in a valid but unspecified (usually empty) state: moving a `std::vector` just steals its buffer pointer instead of copying every element. **`std::move(x)`** does not move anything itself; it casts `x` to an **rvalue reference**, allowing a move to be selected. The **Rule of Zero**: if your class only holds members that manage themselves (`std::string`, `std::vector`, smart pointers), write **none** of the special members; the generated ones do the right thing. The **Rule of Three** (classic) and **Rule of Five** (modern): if you write a destructor, copy constructor or copy assignment because the class manages a raw resource, you probably need all of them, plus the two move operations. Prefer the Rule of Zero by wrapping raw resources in RAII types.

## Rule of zero versus a hand-written resource class

The first class needs no special members; the second needs all five.

```cpp
#include <algorithm>
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>

// Rule of Zero: members manage themselves; copies and moves just work
struct Student {
    std::string name;
    std::vector<int> marks;
};

// Rule of Five: owns a raw buffer (shown for learning; prefer std::vector)
class Buffer {
public:
    explicit Buffer(std::size_t n) : size_(n), data_(new int[n]{}) {}
    ~Buffer() { delete[] data_; }

    Buffer(const Buffer& other) : size_(other.size_), data_(new int[other.size_]) {      // copy
        std::copy(other.data_, other.data_ + size_, data_);
    }
    Buffer& operator=(Buffer other) noexcept {      // copy-and-swap handles copy and move assignment
        swap(other);
        return *this;
    }
    Buffer(Buffer&& other) noexcept                  // move: steal the pointer
        : size_(std::exchange(other.size_, 0)), data_(std::exchange(other.data_, nullptr)) {}

    void swap(Buffer& o) noexcept { std::swap(size_, o.size_); std::swap(data_, o.data_); }

private:
    std::size_t size_;
    int* data_;
};

Student a{"Asha", {91, 88}};
Student b = a;              // copy
Student c = std::move(a);   // move: a.name and a.marks are left valid but unspecified (typically empty)
```

## Photocopying versus handing over a file

Copying a folder means photocopying every page. Moving it means handing the original folder across the desk: instant, and you are left with an empty space where it was.

**Quiz:** What does std::move actually do?

- [ ] It copies the object to the heap
- [x] It casts its argument to an rvalue reference so that a move operation can be chosen
- [ ] It deletes the original object
- [ ] It always makes code faster

*Answer:* It casts its argument to an rvalue reference so that a move operation can be chosen. std::move is a cast; the move constructor or assignment performs the actual transfer.
