Kodax

الوراثة المتعددة — OOP في C++

الوراثة المتعددة (Multiple Inheritance)

C++ تسمح للصنف بالوراثة من أكثر من صنف أساسي:

class Parent1 { };
class Parent2 { };
class Child : public Parent1, public Parent2 { };

الاستخدام الآمن: دمج الواجهات

الاستخدام الأكثر شيوعاً وأماناً: صنف ينفذ عدة قدرات مستقلة:

#include <iostream>
using namespace std;

class Printable { public: virtual void print() const = 0; virtual ~Printable() = default; };
class Savable   { public: virtual bool save() const = 0;  virtual ~Savable() = default; };

class Invoice : public Printable, public Savable {
    int number;
public:
    explicit Invoice(int n) : number(n) {}
    void print() const override { cout << "فاتورة #" << number << '\n'; }
    bool save() const override { cout << "حفظ الفاتورة\n"; return true; }
};

void printIt(const Printable& p) { p.print(); }

int main() {
    Invoice inv(42);
    printIt(inv);      // تُعامل كـ Printable
    inv.save();
}

الغموض (Ambiguity)

إذا ورث الصنف دالتين بنفس الاسم من أبوين، فالاستدعاء غامض:

#include <string>
using std::string;

struct Camera { string id() const { return "CAM"; } };
struct Phone  { string id() const { return "PHN"; } };
struct Smart : Camera, Phone {
    string id() const { return Camera::id() + "+" + Phone::id(); }   // نحسم صراحة
};
// بدون تعريف id في Smart: s.id() خطأ ترجمة (request for member 'id' is ambiguous)

مشكلة الماسة (Diamond Problem)

        Device
       /      \
    Phone    Camera
       \      /
      Smartphone

بالوراثة العادية، Smartphone يحتوي نسختين من Device (واحدة عبر Phone وواحدة عبر Camera): بيانات مكررة، و smartphone.powerOn() غامضة.

الحل: الوراثة الافتراضية

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

class Device {
protected:
    string brand;
public:
    explicit Device(string b) : brand(b) { cout << "Device(" << brand << ")\n"; }
    virtual ~Device() = default;
};
class Phone  : virtual public Device { public: Phone()  : Device("?") {} };
class Camera : virtual public Device { public: Camera() : Device("?") {} };

class Smartphone : public Phone, public Camera {
public:
    // الصنف الأخير في السلسلة هو من يبني Device الافتراضي مباشرة
    Smartphone() : Device("Kodax"), Phone(), Camera() {}
    void show() const { cout << brand << '\n'; }   // لا غموض: نسخة واحدة من Device
};

int main() {
    Smartphone s;   // Device(Kodax) — مرة واحدة فقط
    s.show();
}

نقاط مهمة:

- virtual تُكتب عند الوراثة من القاعدة المشتركة (في Phone و Camera)، لا في Smartphone.

- الصنف الأكثر اشتقاقاً هو من يستدعي مُنشئ القاعدة الافتراضية؛ استدعاءات Phone و Camera لـ Device تُتجاهل.

- للوراثة الافتراضية تكلفة بسيطة في الحجم والسرعة.

نصيحة عملية

- ورِث من واجهات متعددة بحرية.

- تجنب وراثة عدة أصناف تحمل بيانات وتنفيذاً؛ إن ظهرت الماسة فكّر أولاً هل التركيب أنسب، ثم استخدم الوراثة الافتراضية إن لزم.

تمرين

في المثال الكامل احذف virtual من وراثة Phone و Camera. كم مرة يُطبع مُنشئ Device الآن؟ وما الخطأ الذي يظهر عند الوصول إلى brand؟

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

// ===== مثال 1: Interfaces متعددة =====
class ISwimmable {
public:
    virtual void swim() = 0;
    virtual ~ISwimmable() = default;
};

class IFlyable {
public:
    virtual void fly() = 0;
    virtual ~IFlyable() = default;
};

class IWalkable {
public:
    virtual void walk() = 0;
    virtual ~IWalkable() = default;
};

class Duck : public ISwimmable, public IFlyable, public IWalkable {
private:
    string name;
public:
    Duck(const string& n) : name(n) {}
    
    void swim() override {
        cout << "🦆 " << name << " يسبح في البحيرة" << endl;
    }
    
    void fly() override {
        cout << "🦆 " << name << " يطير في السماء" << endl;
    }
    
    void walk() override {
        cout << "🦆 " << name << " يمشي على الأرض" << endl;
    }
};

// ===== مثال 2: Diamond Problem =====
class Device {
protected:
    string brand;
    bool isOn;
public:
    Device(const string& b) : brand(b), isOn(false) {
        cout << "📱 Device Constructor: " << brand << endl;
    }
    
    virtual void turnOn() {
        isOn = true;
        cout << brand << " تم التشغيل" << endl;
    }
    
    virtual void turnOff() {
        isOn = false;
        cout << brand << " تم الإيقاف" << endl;
    }
};

// Virtual inheritance لحل Diamond Problem
class Phone : virtual public Device {
protected:
    string phoneNumber;
public:
    Phone(const string& b, const string& num) 
        : Device(b), phoneNumber(num) {
        cout << "📞 Phone Constructor" << endl;
    }
    
    void call(const string& number) {
        cout << "📞 اتصال بـ " << number << endl;
    }
};

class Camera : virtual public Device {
protected:
    int megapixels;
public:
    Camera(const string& b, int mp) 
        : Device(b), megapixels(mp) {
        cout << "📷 Camera Constructor" << endl;
    }
    
    void takePhoto() {
        cout << "📷 التقاط صورة بـ " << megapixels << " ميجابكسل" << endl;
    }
};

// Smartphone يرث من Phone و Camera (Diamond!)
class Smartphone : public Phone, public Camera {
public:
    Smartphone(const string& b, const string& num, int mp)
        : Device(b),  // يجب استدعاء Device مباشرة!
          Phone(b, num), 
          Camera(b, mp) {
        cout << "📱 Smartphone Constructor" << endl;
    }
    
    void turnOn() override {
        Device::turnOn();
        cout << "🔓 جاري فتح القفل..." << endl;
    }
    
    void showInfo() {
        cout << "\n=== " << brand << " ===" << endl;
        cout << "رقم الهاتف: " << phoneNumber << endl;
        cout << "الكاميرا: " << megapixels << " MP" << endl;
    }
};

int main() {
    // مثال 1: Interfaces
    cout << "=== البطة ===" << endl;
    Duck duck("دونالد");
    duck.swim();
    duck.fly();
    duck.walk();
    
    // مثال 2: Smartphone
    cout << "\n=== الهاتف الذكي ===" << endl;
    Smartphone phone("iPhone", "0512345678", 48);
    
    phone.showInfo();
    phone.turnOn();
    phone.call("0509876543");
    phone.takePhoto();
    phone.turnOff();
    
    // Polymorphism مع interfaces
    cout << "\n=== Polymorphism ===" << endl;
    ISwimmable* swimmer = &duck;
    swimmer->swim();
    
    IFlyable* flyer = &duck;
    flyer->fly();
    
    return 0;
}
اختبر فهمك