Skip to content

⚡ 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

cpp
#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

cpp
#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

cpp
#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)

cpp
// 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ả:

  1. Destructor
  2. Copy constructor
  3. Copy assignment operator
  4. Move constructor
  5. Move assignment operator

Move Constructor Guidelines:

cpp
// 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

cpp
#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

cpp
#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

cpp
#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:

cpp
#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:

cpp
MyClass createObject() {
    return MyClass{args};  // RVO optimizes this
}

Container Operations:

cpp
vec.push_back(std::move(obj));     // Move instead of copy
vec.emplace_back(args...);         // Construct in-place

Resource Transfer:

cpp
std::unique_ptr<T> ptr = std::move(other_ptr);  // Transfer ownership

Chuẩ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!

Khóa học C++ miễn phí