Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

ฟังก์ชัน swap() ใน C++ (สลับค่าตัวแปร)

std::swap() เป็นฟังก์ชันใน Header <algorithm> (C++98) และ <utility> (C++11+) ใช้สลับค่าของ Object สองตัวได้อย่างมีประสิทธิภาพ

Syntax


#include <utility>  // C++11+
// หรือ #include <algorithm>  // C++98

template<class T>
void swap(T& a, T& b) noexcept(/*...*/);

โค้ดตัวอย่างพื้นฐาน

#include <iostream>
#include <utility>  // หรือ <algorithm>
using namespace std;

int main() {
    int x = 35, y = 75;
    
    cout << "Before swap: x = " << x << ", y = " << y << endl;
    swap(x, y);
    cout << "After swap:  x = " << x << ", y = " << y << endl;
    
    return 0;
}

ผลลัพธ์

Before swap: x = 35, y = 75
After swap:  x = 75, y = 35

การทำงานภายใน (Implementation)


// Conceptual Implementation (C++11 Move Semantics)
template<class T>
void swap(T& a, T& b) noexcept(is_nothrow_move_constructible<T>::value && 
                                  is_nothrow_move_assignable<T>::value) {
    T tmp = std::move(a);  // Move a to tmp
    a = std::move(b);      // Move b to a
    b = std::move(tmp);    // Move tmp to b
}

// Pre-C++11 (Copy-based)
template<class T>
void swap(T& a, T& b) {
    T tmp = a;  // Copy
    a = b;      // Copy
    b = tmp;    // Copy
}

Overload สำหรับ Arrays


#include <iostream>
#include <utility>
#include <array>
using namespace std;

int main() {
    int a[3] = {1, 2, 3};
    int b[3] = {4, 5, 6};
    
    swap(a, b);  // std::swap มี Overload สำหรับ Array (C++11)
    
    cout << "a: "; for (int x : a) cout << x << " ";  // 4 5 6
    cout << "\nb: "; for (int x : b) cout << x << " ";  // 1 2 3
    
    // std::array
    array<int, 3> arr1 = {1,2,3}, arr2 = {4,5,6};
    swap(arr1, arr2);
    
    return 0;
}

Custom Swp สำหรับ Class ของตัวเอง (ADL)


#include <iostream>
#include <utility>
#include <string>
using namespace std;

class MyString {
    string data;
public:
    MyString(const string& s = "") : data(s) {}
    
    // Friend swap function (Argument-Dependent Lookup)
    friend void swap(MyString& a, MyString& b) noexcept {
        using std::swap;
        swap(a.data, b.data);  // ใช้ std::swap สำหรับ string
        cout << "MyString::swap called" << endl;
    }
    
    const string& get() const { return data; }
};

int main() {
    MyString s1("Hello"), s2("World");
    cout << "Before: " << s1.get() << ", " << s2.get() << endl;
    swap(s1, s2);  // เรียก MyString::swap ผ่าน ADL
    cout << "After:  " << s1.get() << ", " << s2.get() << endl;
    return 0;
}

ผลลัพธ์

Before: Hello, World
MyString::swap called
After:  World, Hello

Best Practices: Swap Idiom

1. Copy-and-Swap Idiom (สำหรับ Copy Assignment)


class MyClass {
    int* data;
    size_t size;
public:
    MyClass(size_t n = 0) : size(n), data(new int[n]) {}
    ~MyClass() { delete[] data; }
    
    // Copy Constructor
    MyClass(const MyClass& other) : size(other.size), data(new int[other.size]) {
        copy(other.data, other.data + size, data);
    }
    
    // Copy Assignment via Copy-and-Swap
    MyClass& operator=(MyClass other) {  // Pass by Value (Copy)
        swap(*this, other);              // Swap Resources
        return *this;                    // other (old data) ถูก Destroy
    }
    
    // Swap Function
    friend void swap(MyClass& a, MyClass& b) noexcept {
        using std::swap;
        swap(a.data, b.data);
        swap(a.size, b.size);
    }
};

2. Move Semantics + Swap


class Movable {
    int* data;
    size_t size;
public:
    Movable(size_t n = 0) : size(n), data(new int[n]) {}
    ~Movable() { delete[] data; }
    
    // Move Constructor
    Movable(Movable&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }
    
    // Move Assignment
    Movable& operator=(Movable&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
        }
        return *this;
    }
    
    friend void swap(Movable& a, Movable& b) noexcept {
        using std::swap;
        swap(a.data, b.data);
        swap(a.size, b.size);
    }
};

std::swap vs Custom Swap

ด้าน std::swap Custom Swap
Generic Typesทำงานได้ทุก Type ที่ MoveAssignableต้องเขียนเองต่อ Class
Performanceดี (Move Semantics)ดีที่สุด (ทราบโครงสร้างภายใน)
Exception SafetyStrong (noexcept ถ้า Move noexcept)ขึ้นกับการ Implement
ADLหา Custom Swp ใน Namespace เดียวกับ Typeต้องเป็น Friend หรือ Free Function

การใช้งานกับ STL Algorithms


#include <algorithm>
#include <vector>

int main() {
    vector<int> v = {3, 1, 4, 1, 5};
    
    // Reverse ใช้ swap ภายใน
    reverse(v.begin(), v.end());  // {5, 1, 4, 1, 3}
    
    // Sort ใช้ swap (และ move)
    sort(v.begin(), v.end());  // {1, 1, 3, 4, 5}
    
    // Partition
    partition(v.begin(), v.end(), [](int x){ return x % 2 == 0; });
    
    return 0;
}

สรุป

  • std::swap(a, b) สลับค่า a, b แบบ Generic
  • Include <utility> (C++11+) หรือ <algorithm>
  • ใช้ Move Semantics (C++11) → เร็ว ไม่ Copy
  • เขียน Custom Swap ให้ Class ของเอง (Friend + ADL)
  • ใช้ใน Copy-and-Swap Idiom สำหรับ Exception-Safe Assignment
  • STL Algorithms ใช้ swap ภายใน (sort, reverse, partition, etc.)