Post

Move Constructor & Move Assignment

Move Constructor & Move Assignment

Move Constructor & Move Assignment in C++


Prerequisites


1. Why Move Semantics Exist

Copying objects can be expensive:

Move semantics allows transferring ownership instead of copying data.

  • Memory allocation
  • Deep copy of data
  • Performance overhead

Instead of copying resources, move them

Copy vs Move

✔ Copy (expensive)
1
2
std::vector<int> a = {1,2,3};
std::vector<int> b = a;  // copy
  • New memory allocated
  • Data copied
✔ Move (cheap)
1
2
std::vector<int> a = {1,2,3};
std::vector<int> b = std::move(a);  // move
  • Ownership transferred
  • No deep copy

2. Move Constructor

1
ClassName(ClassName&& other);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Buffer 
{
public:
    Buffer(size_t s) : size(s) 
    {
        data = new int[s];
    }

    // Move constructor
    Buffer(Buffer&& other) noexcept 
    {
        data = other.data;
        size = other.size;

        other.data = nullptr;
        other.size = 0;
    }

public:
    int* data;
    size_t size;
};
  • && = rvalue reference
  • Transfer ownership
  • Leave source in safe state

3. Move Assignment Operator

1
ClassName& operator=(ClassName&& other);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Buffer& operator=(Buffer&& other) noexcept 
{
    if (this != &other) 
    {
        delete[] data;  // release current

        data = other.data;
        size = other.size;

        other.data = nullptr;
        other.size = 0;
    }

    return *this;
}

Why noexcept is Important

STL containers rely on it

1
Buffer(Buffer&& other) noexcept;
  • Enables move instead of copy
  • Improves performance

std::move Meaning

1
std::move(obj);
  • Does NOT move by itself
  • Just casts to rvalue
✔ Real meaning
1
static_cast<T&&>(obj);

4. Common Mistakes

❌ Forget to nullify source

1
data = other.data;  // but not resetting other

Double free risk

❌ Overusing std::move

1
return std::move(obj);  // may break copy elision
✔ Correct
1
return obj;
1
2
3
4
5
std::vector<int> create() 
{
    std::vector<int> v(1000);
    return v;  // move or elision
}
When to Use

✔ Large objects
✔ Resource ownership (heap, file, socket)
✔ Performance-critical code

When NOT Needed

✔ Small objects (int, double)
✔ Trivially copyable types

This post is licensed under CC BY 4.0 by the author.