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
- Does NOT move by itself
- Just casts to rvalue
✔ Real meaning
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
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