الوراثة المتعددة — 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;
}اختبر فهمك