⚡ Move Semantics và Rvalue References
📖 Giới thiệu
Move semantics là một tính năng quan trọng được giới thiệu trong C++11, cho phép "di chuyển" tài nguyên thay vì sao chép, giúp tối ưu hóa hiệu suất đáng kể. Đây là một trong những cải tiến lớn nhất của Modern C++, giảm thiểu các copy operations không cần thiết.
Vấn đề với Copy Semantics:
- Expensive copies: Sao chép containers lớn tốn thời gian và bộ nhớ
- Temporary objects: Objects tạm thời được copy rồi destroy
- Resource waste: Không tận dụng objects sắp bị destroy
- Performance bottleneck: Copy operations làm chậm chương trình
Move Semantics giải quyết:
- Resource transfer: "Trộm" tài nguyên thay vì copy
- Efficiency: Tránh allocation/deallocation không cần thiết
- Temporary optimization: Tối ưu với temporary objects
- Zero-cost: Move operations thường rất nhanh (O(1))
Khái niệm cốt lõi:
- Lvalue: Objects có tên, có địa chỉ trong memory
- Rvalue: Temporary objects, literals, expressions
- Rvalue References:
T&&- tham chiếu đến rvalue - std::move: Chuyển lvalue thành rvalue reference
🔧 Cú pháp
Lvalue vs Rvalue
#include <iostream>
#include <string>
using namespace std;
void demonstrateValueCategories() {
string str = "Hello";
// Lvalues - có tên, có địa chỉ
string& lref = str; // Lvalue reference
cout << &str << endl; // Có địa chỉ
// Rvalues - temporary objects
string temp = str + " World"; // str + " World" là rvalue
// string& bad = str + " World"; // ERROR: không thể bind rvalue to lvalue ref
// Rvalue references (C++11)
string&& rref = str + " World"; // OK: rvalue reference
cout << rref << endl; // "Hello World"
// Functions returning rvalue
string&& rref2 = string("Temporary"); // OK
// Literals are rvalues
// int& bad = 42; // ERROR
int&& good = 42; // OK
}Move Constructor và Move Assignment
#include <iostream>
#include <vector>
#include <utility> // for std::move
using namespace std;
class MyVector {
private:
int* data;
size_t size;
size_t capacity;
public:
// Constructor
MyVector(size_t cap = 0) : data(nullptr), size(0), capacity(cap) {
if (capacity > 0) {
data = new int[capacity];
cout << "🔧 Constructor: allocated " << capacity << " ints" << endl;
}
}
// Copy constructor
MyVector(const MyVector& other) : size(other.size), capacity(other.capacity) {
if (capacity > 0) {
data = new int[capacity];
for (size_t i = 0; i < size; i++) {
data[i] = other.data[i];
}
cout << "📋 Copy constructor: copied " << capacity << " ints" << endl;
} else {
data = nullptr;
}
}
// Move constructor (C++11)
MyVector(MyVector&& other) noexcept
: data(other.data), size(other.size), capacity(other.capacity) {
// "Steal" resources from other
other.data = nullptr;
other.size = 0;
other.capacity = 0;
cout << "⚡ Move constructor: stole " << capacity << " ints" << endl;
}
// Copy assignment
MyVector& operator=(const MyVector& other) {
if (this != &other) {
// Clean up existing data
delete[] data;
// Copy from other
size = other.size;
capacity = other.capacity;
if (capacity > 0) {
data = new int[capacity];
for (size_t i = 0; i < size; i++) {
data[i] = other.data[i];
}
cout << "📋 Copy assignment: copied " << capacity << " ints" << endl;
} else {
data = nullptr;
}
}
return *this;
}
// Move assignment (C++11)
MyVector& operator=(MyVector&& other) noexcept {
if (this != &other) {
// Clean up existing data
delete[] data;
// "Steal" from other
data = other.data;
size = other.size;
capacity = other.capacity;
// Reset other
other.data = nullptr;
other.size = 0;
other.capacity = 0;
cout << "⚡ Move assignment: stole " << capacity << " ints" << endl;
}
return *this;
}
// Destructor
~MyVector() {
if (data) {
cout << "🗑️ Destructor: freeing " << capacity << " ints" << endl;
delete[] data;
} else {
cout << "🗑️ Destructor: no data to free" << endl;
}
}
// Utility methods
void push_back(int value) {
if (size >= capacity) {
// Resize logic (simplified)
capacity = capacity == 0 ? 1 : capacity * 2;
int* newData = new int[capacity];
for (size_t i = 0; i < size; i++) {
newData[i] = data[i];
}
delete[] data;
data = newData;
}
data[size++] = value;
}
void print() const {
cout << "[";
for (size_t i = 0; i < size; i++) {
cout << data[i];
if (i < size - 1) cout << ", ";
}
cout << "] (capacity: " << capacity << ")" << endl;
}
};std::move và Perfect Forwarding
#include <utility> // std::move, std::forward
// std::move - cast to rvalue reference
void demonstrateMove() {
MyVector v1(10);
v1.push_back(1);
v1.push_back(2);
cout << "Before move:" << endl;
v1.print();
// Explicit move
MyVector v2 = std::move(v1); // Calls move constructor
cout << "After move:" << endl;
cout << "v1: "; v1.print(); // v1 is now in "moved-from" state
cout << "v2: "; v2.print(); // v2 has the data
}
// Perfect forwarding template
template<typename T>
void wrapper(T&& arg) {
// Forward preserves value category
process(std::forward<T>(arg));
}
// Function overloads to demonstrate forwarding
void process(const string& str) {
cout << "Processing lvalue: " << str << endl;
}
void process(string&& str) {
cout << "Processing rvalue: " << str << endl;
}
void demonstrateForwarding() {
string s = "Hello";
wrapper(s); // Forwards as lvalue
wrapper(string("Hi")); // Forwards as rvalue
wrapper(std::move(s)); // Forwards as rvalue
}Return Value Optimization (RVO)
// Named Return Value Optimization (NRVO)
MyVector createVector(size_t size) {
MyVector result(size); // Local object
for (size_t i = 0; i < size; i++) {
result.push_back(i);
}
return result; // Compiler optimizes away the copy/move
}
// Return Value Optimization (RVO)
MyVector createTempVector() {
return MyVector(5); // Temporary object, RVO applies
}
void demonstrateRVO() {
cout << "=== RVO Demo ===" << endl;
// NRVO - No constructor/destructor for temporary
MyVector v1 = createVector(3);
// RVO - Direct construction in place
MyVector v2 = createTempVector();
v1.print();
v2.print();
}🔬 Phân tích & Giải thích chi tiết
Value Categories trong C++11
Lvalue (Left-value):
- Objects có tên hoặc có thể lấy địa chỉ
- Tồn tại sau expression
- Có thể xuất hiện bên trái assignment
Rvalue (Right-value):
- Temporary objects, literals
- Không có tên, không thể lấy địa chỉ
- Sắp bị destroy
Xvalue (eXpiring value):
- Objects được cast về rvalue reference
std::move(obj)tạo ra xvalue
Move Semantics Implementation Rules
Rule of Five (C++11): Nếu class define một trong những methods sau, nên define tất cả:
- Destructor
- Copy constructor
- Copy assignment operator
- Move constructor
- Move assignment operator
Move Constructor Guidelines:
// Always mark noexcept if possible
MyClass(MyClass&& other) noexcept {
// Transfer ownership
member = other.member;
other.member = nullptr; // Reset source
}noexcept Specification:
- Move operations nên là
noexcept - STL containers chỉ dùng move nếu nó
noexcept - Nếu không
noexcept, STL sẽ fallback về copy
Performance Implications
When Move Happens:
- Return from function (if no RVO)
- Passing temporary objects
std::move()explicit cast- Container operations (resize, etc.)
When Copy Still Happens:
- Move constructor/assignment không available
- Move operations có thể throw exceptions
- Lvalue được pass mà không dùng
std::move
💻 Ví dụ minh họa
Ví dụ 1: String Builder với Move Optimization
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <utility>
using namespace std;
class StringBuilder {
private:
vector<string> parts;
public:
// Add string với copy
StringBuilder& add(const string& str) {
parts.push_back(str); // Copy
return *this;
}
// Add string với move
StringBuilder& add(string&& str) {
parts.push_back(std::move(str)); // Move
return *this;
}
// Template version with perfect forwarding
template<typename T>
StringBuilder& append(T&& str) {
parts.push_back(std::forward<T>(str));
return *this;
}
// Build final string với move optimization
string build() && { // Rvalue-qualified method
string result;
size_t totalSize = 0;
// Calculate total size
for (const auto& part : parts) {
totalSize += part.size();
}
// Reserve space
result.reserve(totalSize);
// Move all parts
for (auto& part : parts) {
result += std::move(part); // Move instead of copy
}
return result;
}
// Build with copy (for lvalue objects)
string build() const & {
string result;
for (const auto& part : parts) {
result += part; // Copy
}
return result;
}
size_t partCount() const { return parts.size(); }
void printParts() const {
cout << "StringBuilder parts:" << endl;
for (size_t i = 0; i < parts.size(); i++) {
cout << i << ": \"" << parts[i] << "\"" << endl;
}
}
};
// Factory function returning StringBuilder
StringBuilder createBuilder() {
StringBuilder builder;
builder.add("Hello")
.add(" ")
.add("World")
.add("!")
.add(" This")
.add(" is")
.add(" a")
.add(" test.");
return builder; // RVO will optimize this
}
void demonstrateStringBuilder() {
cout << "=== STRING BUILDER MOVE OPTIMIZATION ===" << endl;
// Test with temporary objects (rvalues)
cout << "\n1. Building with temporary object (move optimized):" << endl;
string result1 = createBuilder().build(); // Calls rvalue build()
cout << "Result: " << result1 << endl;
// Test with lvalue object
cout << "\n2. Building with lvalue object (copy version):" << endl;
StringBuilder builder = createBuilder();
string result2 = builder.build(); // Calls lvalue build()
cout << "Result: " << result2 << endl;
cout << "Builder still has " << builder.partCount() << " parts" << endl;
// Test perfect forwarding
cout << "\n3. Testing perfect forwarding:" << endl;
StringBuilder perfectBuilder;
string lvalue = "Lvalue";
perfectBuilder.append(lvalue) // Forwards as lvalue (copy)
.append(string("Temporary")) // Forwards as rvalue (move)
.append(std::move(lvalue)); // Explicit move
cout << "After operations, lvalue = \"" << lvalue << "\"" << endl;
string result3 = std::move(perfectBuilder).build();
cout << "Result: " << result3 << endl;
// Performance comparison
cout << "\n4. Performance comparison:" << endl;
auto start = chrono::high_resolution_clock::now();
// Build 1000 strings with move optimization
for (int i = 0; i < 1000; i++) {
string fast = createBuilder().build(); // Move-optimized
}
auto end = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
cout << "Move-optimized: " << duration.count() << " microseconds" << endl;
}
int main() {
demonstrateStringBuilder();
return 0;
}Ví dụ 2: Resource Manager với RAII và Move
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <utility>
using namespace std;
// Resource class representing expensive objects
class Resource {
private:
string name;
vector<int> data;
static int nextId;
int id;
public:
Resource(const string& n, size_t dataSize = 1000)
: name(n), data(dataSize, 42), id(nextId++) {
cout << "🔧 Resource[" << id << "] \"" << name << "\" created (" << dataSize << " ints)" << endl;
}
~Resource() {
cout << "🗑️ Resource[" << id << "] \"" << name << "\" destroyed" << endl;
}
// Copy constructor
Resource(const Resource& other)
: name(other.name + "_copy"), data(other.data), id(nextId++) {
cout << "📋 Resource[" << id << "] copied from [" << other.id << "]" << endl;
}
// Move constructor
Resource(Resource&& other) noexcept
: name(std::move(other.name)), data(std::move(other.data)), id(other.id) {
other.id = -1; // Mark as moved
cout << "⚡ Resource[" << id << "] moved from [" << other.id << "]" << endl;
}
// Copy assignment
Resource& operator=(const Resource& other) {
if (this != &other) {
name = other.name + "_copy";
data = other.data;
cout << "📋 Resource[" << id << "] copy-assigned from [" << other.id << "]" << endl;
}
return *this;
}
// Move assignment
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
name = std::move(other.name);
data = std::move(other.data);
cout << "⚡ Resource[" << id << "] move-assigned from [" << other.id << "]" << endl;
other.id = -1;
}
return *this;
}
const string& getName() const { return name; }
int getId() const { return id; }
size_t getDataSize() const { return data.size(); }
void process() const {
cout << "🔄 Processing Resource[" << id << "] \"" << name << "\"" << endl;
}
};
int Resource::nextId = 1;
// RAII Resource Manager
class ResourceManager {
private:
vector<unique_ptr<Resource>> resources;
string managerName;
public:
ResourceManager(const string& name) : managerName(name) {
cout << "🏭 ResourceManager \"" << managerName << "\" created" << endl;
}
~ResourceManager() {
cout << "🏭 ResourceManager \"" << managerName << "\" destroyed" << endl;
}
// Move-only semantics (no copy)
ResourceManager(const ResourceManager&) = delete;
ResourceManager& operator=(const ResourceManager&) = delete;
// Move constructor
ResourceManager(ResourceManager&& other) noexcept
: resources(std::move(other.resources)), managerName(std::move(other.managerName)) {
cout << "⚡ ResourceManager moved" << endl;
}
// Move assignment
ResourceManager& operator=(ResourceManager&& other) noexcept {
if (this != &other) {
resources = std::move(other.resources);
managerName = std::move(other.managerName);
cout << "⚡ ResourceManager move-assigned" << endl;
}
return *this;
}
// Add resource by value (enables move)
void addResource(Resource resource) {
auto ptr = make_unique<Resource>(std::move(resource));
resources.push_back(std::move(ptr));
cout << "✅ Added Resource[" << ptr->getId() << "] to manager" << endl;
}
// Create resource in-place
template<typename... Args>
void emplaceResource(Args&&... args) {
auto ptr = make_unique<Resource>(std::forward<Args>(args)...);
resources.push_back(std::move(ptr));
cout << "✅ Emplaced Resource[" << ptr->getId() << "] in manager" << endl;
}
// Get resource count
size_t getResourceCount() const {
return resources.size();
}
// Process all resources
void processAll() const {
cout << "\n🔄 Processing all resources in \"" << managerName << "\":" << endl;
for (const auto& resource : resources) {
resource->process();
}
}
// Move resource to another manager
unique_ptr<Resource> moveResource(size_t index) {
if (index < resources.size()) {
auto resource = std::move(resources[index]);
resources.erase(resources.begin() + index);
cout << "📤 Moved out Resource[" << resource->getId() << "]" << endl;
return resource;
}
return nullptr;
}
void showStatus() const {
cout << "📊 Manager \"" << managerName << "\" has " << resources.size() << " resources" << endl;
}
};
// Factory functions
Resource createExpensiveResource(const string& name) {
cout << "🏗️ Factory creating expensive resource..." << endl;
Resource resource(name, 10000); // Large data size
// Some expensive initialization
for (int i = 0; i < 1000; i++) {
// Simulate work
}
return resource; // RVO will optimize this
}
ResourceManager createResourceManager(const string& name) {
ResourceManager manager(name);
// Add some resources
manager.addResource(createExpensiveResource("FactoryResource1"));
manager.emplaceResource("FactoryResource2", 5000);
return manager; // RVO
}
int main() {
cout << "=== RESOURCE MANAGER MOVE SEMANTICS DEMO ===" << endl;
// Test 1: Basic move operations
cout << "\n=== TEST 1: Basic Move Operations ===" << endl;
{
Resource r1("Original", 100);
// Move constructor
Resource r2 = std::move(r1);
cout << "r1 name after move: \"" << r1.getName() << "\"" << endl;
cout << "r2 name: \"" << r2.getName() << "\"" << endl;
// Move assignment
Resource r3("Target", 50);
r3 = createExpensiveResource("FromFactory");
}
// Test 2: ResourceManager with moves
cout << "\n=== TEST 2: ResourceManager Operations ===" << endl;
{
ResourceManager manager("MainManager");
// Add resource via move
Resource temp("Temporary", 200);
manager.addResource(std::move(temp));
cout << "temp name after move: \"" << temp.getName() << "\"" << endl;
// Emplace resource
manager.emplaceResource("EmplacedResource", 300);
manager.showStatus();
manager.processAll();
}
// Test 3: Factory and RVO
cout << "\n=== TEST 3: Factory Functions and RVO ===" << endl;
{
ResourceManager manager = createResourceManager("FactoryManager");
manager.showStatus();
manager.processAll();
// Move manager
ResourceManager movedManager = std::move(manager);
cout << "\nAfter move:" << endl;
manager.showStatus(); // Original manager
movedManager.showStatus(); // Moved manager
}
// Test 4: Container move optimization
cout << "\n=== TEST 4: Container Move Optimization ===" << endl;
{
vector<Resource> resources;
resources.reserve(3); // Avoid reallocation
cout << "Adding resources to vector:" << endl;
resources.push_back(createExpensiveResource("VectorResource1"));
resources.emplace_back("VectorResource2", 150);
Resource temp("TempResource", 75);
resources.push_back(std::move(temp));
cout << "\nProcessing vector resources:" << endl;
for (const auto& resource : resources) {
resource.process();
}
}
cout << "\n=== DEMO COMPLETED ===" << endl;
return 0;
}Ví dụ 3: Performance Benchmark - Copy vs Move
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <utility>
#include <algorithm>
using namespace std;
class BenchmarkObject {
private:
vector<int> data;
string name;
static int copyCount;
static int moveCount;
public:
BenchmarkObject(const string& n, size_t size = 1000)
: data(size, 42), name(n) {}
// Copy constructor
BenchmarkObject(const BenchmarkObject& other)
: data(other.data), name(other.name + "_copy") {
copyCount++;
}
// Move constructor
BenchmarkObject(BenchmarkObject&& other) noexcept
: data(std::move(other.data)), name(std::move(other.name)) {
moveCount++;
}
// Copy assignment
BenchmarkObject& operator=(const BenchmarkObject& other) {
if (this != &other) {
data = other.data;
name = other.name + "_copy";
copyCount++;
}
return *this;
}
// Move assignment
BenchmarkObject& operator=(BenchmarkObject&& other) noexcept {
if (this != &other) {
data = std::move(other.data);
name = std::move(other.name);
moveCount++;
}
return *this;
}
const string& getName() const { return name; }
size_t getDataSize() const { return data.size(); }
static void resetCounters() {
copyCount = moveCount = 0;
}
static void printStats() {
cout << "📊 Copies: " << copyCount << ", Moves: " << moveCount << endl;
}
};
int BenchmarkObject::copyCount = 0;
int BenchmarkObject::moveCount = 0;
// Benchmark functions
template<typename T>
void timeOperation(const string& description, T operation) {
BenchmarkObject::resetCounters();
auto start = chrono::high_resolution_clock::now();
operation();
auto end = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
cout << "⏱️ " << description << ": " << duration.count() << " μs ";
BenchmarkObject::printStats();
}
void vectorCopyBenchmark() {
const size_t NUM_OBJECTS = 1000;
const size_t OBJECT_SIZE = 10000;
cout << "\n=== VECTOR COPY BENCHMARK ===" << endl;
cout << "Objects: " << NUM_OBJECTS << ", Object size: " << OBJECT_SIZE << " ints" << endl;
// Create source vector
vector<BenchmarkObject> sourceVec;
sourceVec.reserve(NUM_OBJECTS);
for (size_t i = 0; i < NUM_OBJECTS; i++) {
sourceVec.emplace_back("Object" + to_string(i), OBJECT_SIZE);
}
// Test 1: Copy vector
timeOperation("Copy vector", [&]() {
vector<BenchmarkObject> copyVec = sourceVec; // Copy all objects
});
// Test 2: Move vector
timeOperation("Move vector", [&]() {
vector<BenchmarkObject> tempVec = sourceVec; // Create temp copy
vector<BenchmarkObject> moveVec = std::move(tempVec); // Move vector
});
// Test 3: Move individual elements
timeOperation("Move elements", [&]() {
vector<BenchmarkObject> tempVec = sourceVec; // Create temp copy
vector<BenchmarkObject> moveVec;
moveVec.reserve(NUM_OBJECTS);
for (auto& obj : tempVec) {
moveVec.push_back(std::move(obj));
}
});
}
void containerOperationsBenchmark() {
const size_t NUM_OPERATIONS = 10000;
cout << "\n=== CONTAINER OPERATIONS BENCHMARK ===" << endl;
// Test vector push_back
timeOperation("vector::push_back copy", [&]() {
vector<BenchmarkObject> vec;
for (size_t i = 0; i < NUM_OPERATIONS; i++) {
BenchmarkObject obj("Object" + to_string(i), 100);
vec.push_back(obj); // Copy
}
});
timeOperation("vector::push_back move", [&]() {
vector<BenchmarkObject> vec;
for (size_t i = 0; i < NUM_OPERATIONS; i++) {
BenchmarkObject obj("Object" + to_string(i), 100);
vec.push_back(std::move(obj)); // Move
}
});
timeOperation("vector::emplace_back", [&]() {
vector<BenchmarkObject> vec;
for (size_t i = 0; i < NUM_OPERATIONS; i++) {
vec.emplace_back("Object" + to_string(i), 100); // Construct in place
}
});
}
void sortingBenchmark() {
const size_t NUM_OBJECTS = 5000;
cout << "\n=== SORTING BENCHMARK ===" << endl;
// Create vector for sorting
vector<BenchmarkObject> originalVec;
originalVec.reserve(NUM_OBJECTS);
for (size_t i = 0; i < NUM_OBJECTS; i++) {
originalVec.emplace_back("Object" + to_string(NUM_OBJECTS - i), 1000);
}
// Test sorting (move semantics automatically used)
timeOperation("std::sort with move", [&]() {
vector<BenchmarkObject> sortVec = originalVec; // Copy for sorting
sort(sortVec.begin(), sortVec.end(), [](const BenchmarkObject& a, const BenchmarkObject& b) {
return a.getName() < b.getName();
});
});
}
// Perfect forwarding benchmark
template<typename... Args>
BenchmarkObject createObject(Args&&... args) {
return BenchmarkObject(std::forward<Args>(args)...);
}
void perfectForwardingBenchmark() {
cout << "\n=== PERFECT FORWARDING BENCHMARK ===" << endl;
timeOperation("Direct construction", [&]() {
for (int i = 0; i < 1000; i++) {
BenchmarkObject obj("Direct" + to_string(i), 500);
}
});
timeOperation("Perfect forwarding", [&]() {
for (int i = 0; i < 1000; i++) {
BenchmarkObject obj = createObject("Forwarded" + to_string(i), 500);
}
});
}
int main() {
cout << "=== MOVE SEMANTICS PERFORMANCE BENCHMARK ===" << endl;
vectorCopyBenchmark();
containerOperationsBenchmark();
sortingBenchmark();
perfectForwardingBenchmark();
cout << "\n🎯 Key Takeaways:" << endl;
cout << "- Move operations are significantly faster than copies" << endl;
cout << "- emplace_back avoids temporary object creation" << endl;
cout << "- STL algorithms automatically use move semantics" << endl;
cout << "- Perfect forwarding eliminates unnecessary copies" << endl;
return 0;
}🏋️ Thực hành
Bài tập 1: Custom Container với Move Optimization
Implement một custom vector class với full move semantics:
- Move constructor và move assignment
- Move-aware methods (push_back, insert, etc.)
- Performance comparison với std::vector
Bài tập 2: RAII Wrapper với Move-Only Semantics
Tạo RAII wrapper cho file handles:
- Move-only semantics (không thể copy)
- Automatic resource cleanup
- Exception safety
Bài tập 3: String Class với Small String Optimization
Implement custom string class:
- Small String Optimization (SSO)
- Move semantics cho cả small và large strings
- Performance benchmarking
Bài tập 4: Function Object với Move Capture
Tạo lambda/function object với move capture:
- Capture expensive objects by move
- Compare performance với copy capture
- Use with std::function
Lời giải chi tiết
Bài tập 1 - Custom Vector:
#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
template<typename T>
class MoveVector {
private:
T* data_;
size_t size_;
size_t capacity_;
void reallocate(size_t newCapacity) {
T* newData = static_cast<T*>(::operator new(newCapacity * sizeof(T)));
// Move construct elements
for (size_t i = 0; i < size_; i++) {
new (newData + i) T(std::move(data_[i]));
data_[i].~T();
}
::operator delete(data_);
data_ = newData;
capacity_ = newCapacity;
}
public:
// Constructor
MoveVector() : data_(nullptr), size_(0), capacity_(0) {}
explicit MoveVector(size_t capacity)
: data_(static_cast<T*>(::operator new(capacity * sizeof(T)))),
size_(0), capacity_(capacity) {}
// Destructor
~MoveVector() {
clear();
::operator delete(data_);
}
// Move constructor
MoveVector(MoveVector&& other) noexcept
: data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
// Move assignment
MoveVector& operator=(MoveVector&& other) noexcept {
if (this != &other) {
clear();
::operator delete(data_);
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
return *this;
}
// Disable copy
MoveVector(const MoveVector&) = delete;
MoveVector& operator=(const MoveVector&) = delete;
// Push back with move
void push_back(T&& value) {
if (size_ >= capacity_) {
reallocate(capacity_ == 0 ? 1 : capacity_ * 2);
}
new (data_ + size_) T(std::move(value));
size_++;
}
// Emplace back
template<typename... Args>
void emplace_back(Args&&... args) {
if (size_ >= capacity_) {
reallocate(capacity_ == 0 ? 1 : capacity_ * 2);
}
new (data_ + size_) T(std::forward<Args>(args)...);
size_++;
}
void clear() {
for (size_t i = 0; i < size_; i++) {
data_[i].~T();
}
size_ = 0;
}
size_t size() const { return size_; }
size_t capacity() const { return capacity_; }
T& operator[](size_t index) { return data_[index]; }
const T& operator[](size_t index) const { return data_[index]; }
};📋 Tóm tắt
Các điểm quan trọng
Move Semantics Core Concepts:
- Rvalue References:
T&&- tham chiếu đến temporary objects - std::move: Cast object thành rvalue reference
- std::forward: Perfect forwarding trong templates
- Move Constructor/Assignment: Transfer ownership thay vì copy
Performance Benefits:
- Avoid Expensive Copies: Đặc biệt với containers, strings
- Resource Transfer: "Steal" resources từ temporary objects
- Container Optimization: STL containers tự động dùng move
- Return Value Optimization: Compiler optimizations
Best Practices:
- Rule of Five: Implement tất cả special member functions
- noexcept: Mark move operations as noexcept
- Move-Only Types: unique_ptr, file handles, etc.
- Perfect Forwarding: Trong generic code
Common Patterns
Factory Functions:
MyClass createObject() {
return MyClass{args}; // RVO optimizes this
}Container Operations:
vec.push_back(std::move(obj)); // Move instead of copy
vec.emplace_back(args...); // Construct in-placeResource Transfer:
std::unique_ptr<T> ptr = std::move(other_ptr); // Transfer ownershipChuẩn bị cho bài tiếp theo
Bài học tiếp theo sẽ tìm hiểu về Memory Management nâng cao:
- Custom Allocators: Tối ưu memory allocation
- Memory Pools: Pre-allocated memory management
- Alignment: Memory alignment và padding
- NUMA: Non-Uniform Memory Access optimization
- Memory Profiling: Tools và techniques để analyze memory usage
Move semantics là nền tảng của high-performance C++. Hãy luyện tập để viết code hiệu quả và tận dụng Modern C++ features!