← Back to C++ Course | Chapter 16: Modern C++ | Lesson 4 of 9

C++ Move Semantics

What is Move Semantics?

Move semantics lets the compiler transfer ownership of a resource -- like heap-allocated memory inside a std::vector or std::string -- from a temporary object directly into a new one, instead of performing a full deep copy. For large objects, this turns an O(n) copy into an O(1) pointer swap.

Example: What is Move Semantics?

cpp
#include <iostream>
#include <vector>

int main() {
	std::vector<int> source = {1, 2, 3};
	std::vector<int> destination = std::move(source);
	std::cout << destination.size() << std::endl;
	return 0;
}

The std::move Function

std::move() doesn't physically move any data by itself -- it's purely a cast that tells the compiler "treat this lvalue as an rvalue," making it eligible to be moved from. The actual transfer of ownership happens inside whatever move constructor or move assignment operator subsequently receives it.

Example: The std::move Function

cpp
#include <iostream>
#include <string>
#include <utility>

int main() {
	std::string a = "Hello";
	std::string b = std::move(a);
	std::cout << b << std::endl;
	return 0;
}

Move Constructor

A move constructor takes ownership of a temporary object's internal resources (like a raw pointer to heap memory) by copying that pointer into the new object, then setting the temporary's own pointer to nullptr so its destructor won't accidentally free memory the new object now owns.

Example: Move Constructor

cpp
#include <iostream>

class Buffer {
public:
	int *data;
	Buffer(int val) { data = new int(val); }
	Buffer(Buffer &&other) noexcept {
		data = other.data;
		other.data = nullptr;
	}
	~Buffer() { delete data; }
};

int main() {
	Buffer a(5);
	Buffer b(std::move(a));
	std::cout << *b.data << std::endl;
	return 0;
}

Move Assignment Operator

The move assignment operator first releases whatever resources the target object currently holds (to avoid a leak), then steals the source object's resources the same way a move constructor does -- this is why move assignment is typically written to reuse the move constructor's logic.

Example: Move Assignment Operator

cpp
#include <iostream>

class Buffer {
public:
	int *data;
	Buffer(int val) { data = new int(val); }
	Buffer &operator=(Buffer &&other) noexcept {
		delete data;
		data = other.data;
		other.data = nullptr;
		return *this;
	}
	~Buffer() { delete data; }
};

int main() {
	Buffer a(5), b(10);
	b = std::move(a);
	std::cout << *b.data << std::endl;
	return 0;
}

Performance Benefits

For types holding large buffers -- strings, vectors, and similar containers -- move semantics avoids the deep copy those types would otherwise require, which matters most when returning large objects from functions or storing them in containers that resize and relocate elements.

Example: Performance Benefits

cpp
#include <iostream>
#include <string>

std::string buildGreeting() {
	std::string result = "Hello, World!";
	return result;
}

int main() {
	std::string message = buildGreeting();
	std::cout << message << std::endl;
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.