Kodax

Operator Overloading — OOP في C++

تحميل العوامل (Operator Overloading)

تحميل العوامل يجعل الكود الذي يستخدم أصنافك طبيعياً: a + b بدلاً من a.add(b). لكنه مجرد دالة باسم خاص: a + b تعني operator+(a, b) أو a.operator+(b).

القاعدة الذهبية

حمّل العامل فقط إذا كان معناه بديهياً ومتوقعاً. + لمتجهين واضح؛ + لإضافة موظف إلى قسم غير واضح — استخدم دالة باسم.

مثال متكامل: Vec2

#include <iostream>
using namespace std;

class Vec2 {
public:
    double x = 0, y = 0;
    Vec2() = default;
    Vec2(double x, double y) : x(x), y(y) {}

    // العوامل المركبة تعدّل الكائن ← عضو، وتُرجع مرجعاً
    Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; }
    Vec2& operator*=(double k) { x *= k; y *= k; return *this; }

    Vec2 operator-() const { return {-x, -y}; }          // سالب أحادي

    bool operator==(const Vec2&) const = default;        // C++20: يولّد == و != تلقائياً
};

// العوامل الثنائية المتماثلة ← دوال حرة مبنية على المركبة
Vec2 operator+(Vec2 a, const Vec2& b) { a += b; return a; }
Vec2 operator*(Vec2 v, double k) { v *= k; return v; }
Vec2 operator*(double k, Vec2 v) { v *= k; return v; }   // ليعمل 2 * v أيضاً

// << يجب أن تكون حرة: الطرف الأيسر ostream وليس Vec2
ostream& operator<<(ostream& os, const Vec2& v) {
    return os << "(" << v.x << ", " << v.y << ")";
}

int main() {
    Vec2 a(1, 2), b(3, 4);
    cout << a + b << '\n';        // (4, 6)
    cout << 2 * a << '\n';        // (2, 4)
    cout << -b << '\n';           // (-3, -4)
    a += b;
    cout << boolalpha << (a == Vec2(4, 6)) << '\n';   // true
}

عضو أم دالة حرة؟

| العامل | الشكل المفضل | السبب |

|---|---|---|

| = [] () -> | عضو (إجباري) | قاعدة اللغة |

| += -= *= | عضو | يعدّل الكائن الأيسر |

| + - * / | حرة | ليعمل التحويل على الطرفين (2 * v) |

| << >> للتدفقات | حرة | الطرف الأيسر ostream/istream |

| == <=> | عضو = default أو حرة | C++20 يولّد الباقي |

بناء + على += يمنع تكرار المنطق.

المقارنة في C++20

auto operator<=>(const T&) const = default; يولّد < <= > >= بمقارنة الأعضاء بالترتيب، و = default على == يولّد == و !=. قبل C++20 كان يلزم كتابة ستة عوامل.

#include <compare>
#include <string>
using namespace std;

struct Version {
    int major, minor, patch;
    auto operator<=>(const Version&) const = default;
};
// Version{1,2,0} < Version{1,10,0}  → true

عامل الفهرسة []

وفّر نسختين: واحدة للكائنات العادية تُرجع مرجعاً قابلاً للتعديل، وواحدة const:

#include <cstddef>

class Row {
    int cells[4] = {};
public:
    int& operator[](std::size_t i) { return cells[i]; }
    const int& operator[](std::size_t i) const { return cells[i]; }
};

قيود

- لا يمكن تحميل: :: . .* ?: sizeof.

- لا يمكن تغيير الأولوية ولا عدد المعاملات ولا اختراع عوامل جديدة.

- لا تحمّل && و ||: النسخة المحمّلة تفقد التقييم المختصر (short-circuit).

تمرين

اكتب صنف Fraction (بسط ومقام) مع + و * و == و <<. اختصر الكسر في المُنشئ باستخدام std::gcd من <numeric>، وتأكد أن Fraction(1,2) == Fraction(2,4).

#include <iostream>
#include <cmath>
using namespace std;

class Vector2D {
private:
    double x, y;

public:
    Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
    
    // Getters
    double getX() const { return x; }
    double getY() const { return y; }
    
    // الجمع
    Vector2D operator+(const Vector2D& other) const {
        return Vector2D(x + other.x, y + other.y);
    }
    
    // الطرح
    Vector2D operator-(const Vector2D& other) const {
        return Vector2D(x - other.x, y - other.y);
    }
    
    // الضرب في عدد
    Vector2D operator*(double scalar) const {
        return Vector2D(x * scalar, y * scalar);
    }
    
    // القسمة على عدد
    Vector2D operator/(double scalar) const {
        if (scalar == 0) throw runtime_error("قسمة على صفر!");
        return Vector2D(x / scalar, y / scalar);
    }
    
    // المساواة
    bool operator==(const Vector2D& other) const {
        return x == other.x && y == other.y;
    }
    
    bool operator!=(const Vector2D& other) const {
        return !(*this == other);
    }
    
    // +=
    Vector2D& operator+=(const Vector2D& other) {
        x += other.x;
        y += other.y;
        return *this;
    }
    
    // السالب
    Vector2D operator-() const {
        return Vector2D(-x, -y);
    }
    
    // الفهرسة
    double operator[](int index) const {
        if (index == 0) return x;
        if (index == 1) return y;
        throw out_of_range("فهرس غير صالح!");
    }
    
    // الطول
    double magnitude() const {
        return sqrt(x*x + y*y);
    }
    
    // Friend للطباعة
    friend ostream& operator<<(ostream& os, const Vector2D& v);
    friend istream& operator>>(istream& is, Vector2D& v);
};

// عامل الطباعة
ostream& operator<<(ostream& os, const Vector2D& v) {
    os << "(" << v.x << ", " << v.y << ")";
    return os;
}

// عامل الإدخال
istream& operator>>(istream& is, Vector2D& v) {
    is >> v.x >> v.y;
    return is;
}

// ضرب عدد في Vector
Vector2D operator*(double scalar, const Vector2D& v) {
    return v * scalar;
}

int main() {
    Vector2D v1(3, 4);
    Vector2D v2(1, 2);
    
    cout << "v1 = " << v1 << endl;
    cout << "v2 = " << v2 << endl;
    
    // العمليات الحسابية
    cout << "\n=== العمليات ===" << endl;
    cout << "v1 + v2 = " << (v1 + v2) << endl;
    cout << "v1 - v2 = " << (v1 - v2) << endl;
    cout << "v1 * 2 = " << (v1 * 2) << endl;
    cout << "3 * v2 = " << (3 * v2) << endl;
    cout << "v1 / 2 = " << (v1 / 2) << endl;
    cout << "-v1 = " << (-v1) << endl;
    
    // المقارنة
    cout << "\n=== المقارنة ===" << endl;
    cout << "v1 == v2: " << (v1 == v2 ? "نعم" : "لا") << endl;
    cout << "v1 != v2: " << (v1 != v2 ? "نعم" : "لا") << endl;
    
    // الفهرسة
    cout << "\n=== الفهرسة ===" << endl;
    cout << "v1[0] = " << v1[0] << endl;
    cout << "v1[1] = " << v1[1] << endl;
    
    // +=
    cout << "\n=== += ===" << endl;
    v1 += v2;
    cout << "v1 بعد += v2: " << v1 << endl;
    
    // الطول
    cout << "\n=== الطول ===" << endl;
    Vector2D v3(3, 4);
    cout << "|" << v3 << "| = " << v3.magnitude() << endl;
    
    return 0;
}
اختبر فهمك