Kodax

الوراثة Inheritance — OOP في C++

الوراثة (Inheritance)

الوراثة تعني أن صنفاً مشتقاً (derived) يأخذ أعضاء صنف أساسي (base) ويضيف إليها. استخدمها عندما تصح جملة "هو نوع من" (is-a): الكلب نوع من الحيوانات، الحساب الادخاري نوع من الحسابات.

الصيغة

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

class Animal {
protected:                       // متاح للأصناف المشتقة، مخفي عن الخارج
    string name;
public:
    explicit Animal(string n) : name(n) { cout << "Animal(" << name << ")\n"; }
    ~Animal() { cout << "~Animal\n"; }
    void eat() const { cout << name << " يأكل\n"; }
};

class Dog : public Animal {
    string breed;
public:
    Dog(string n, string b) : Animal(n), breed(b) {   // استدعاء مُنشئ الأب أولاً
        cout << "Dog(" << breed << ")\n";
    }
    ~Dog() { cout << "~Dog\n"; }
    void bark() const { cout << name << " ينبح\n"; }   // يصل إلى name لأنها protected
};

int main() {
    Dog d("ريكس", "جيرمن شيبرد");
    d.eat();     // موروثة
    d.bark();    // خاصة بـ Dog
}
// الناتج: Animal(ريكس) ← Dog(...) ← ... ← ~Dog ← ~Animal

ترتيب البناء والهدم

- البناء: الأب أولاً، ثم أعضاء الابن، ثم جسم مُنشئ الابن (الأساس قبل البناء فوقه).

- الهدم: بالعكس تماماً: الابن أولاً ثم الأب.

- إذا لم تستدعِ مُنشئ الأب صراحةً في قائمة التهيئة، يُستدعى مُنشئه الافتراضي — وإن لم يوجد، خطأ ترجمة.

أنواع الوراثة

| الصيغة | public في الأب يصبح | الاستخدام |

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

| : public Base | public | الوراثة الطبيعية (is-a) — استخدمها في 99% من الحالات |

| : protected Base | protected | نادر |

| : private Base | private | "مُنفَّذ باستخدام" — التركيب غالباً أوضح |

انتبه: class D : Base بدون كلمة تعني private (لأنها class)!

protected: بحذر

protected يجعل الأصناف المشتقة تعتمد على تفاصيل الأب الداخلية. القاعدة العملية: البيانات private، وإن احتاجها الأبناء فوفّر لهم دوال protected.

استدعاء نسخة الأب وإخفاء الأسماء

إذا عرّف الابن دالة بنفس اسم دالة في الأب، فهي تخفي كل نسخ الأب بذلك الاسم:

#include <iostream>
using namespace std;

class Base {
public:
    void show() const { cout << "Base\n"; }
    void show(int n) const { cout << "Base " << n << '\n'; }
};

class Derived : public Base {
public:
    using Base::show;                 // بدونها: d.show(5) خطأ ترجمة (مخفية)
    void show() const {
        Base::show();                 // استدعاء نسخة الأب صراحة
        cout << "Derived\n";
    }
};

وراثة المُنشئات

using Base::Base; داخل الابن يجعله يرث مُنشئات الأب كما هي — مفيد عندما لا يضيف الابن أعضاء تحتاج تهيئة.

final

class Circle final : public Shape تمنع أي وراثة من Circle. ويمكن وضع final على دالة افتراضية لمنع إعادة تعريفها.

الوراثة ليست دائماً الحل

الوراثة أقوى رابط بين صنفين. إذا كانت العلاقة "يملك" (has-a) — السيارة تملك محركاً — فاستخدم التركيب: اجعل المحرك عضواً داخل السيارة. ورِث فقط عندما تريد أن يُستخدم الابن في مكان الأب — وهذا ما يفتحه الدرس التالي: تعدد الأشكال.

تمرين

اكتب Account (رصيد، deposit، withdraw) ثم SavingsAccount : public Account يضيف نسبة فائدة و addInterest(). اطبع رسائل في المُنشئات والمدمّرات وتأكد من الترتيب.

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

// Base class
class Animal {
protected:
    string name;
    int age;

public:
    Animal(string n, int a) : name(n), age(a) {}
    
    virtual void speak() const {
        cout << "صوت ما..." << endl;
    }
    
    void info() const {
        cout << name << " عمره " << age << endl;
    }
    
    virtual ~Animal() {}  // Virtual destructor
};

// Derived class
class Dog : public Animal {
private:
    string breed;

public:
    Dog(string n, int a, string b) 
        : Animal(n, a), breed(b) {}
    
    void speak() const override {
        cout << "هاو هاو! 🐕" << endl;
    }
    
    void fetch() const {
        cout << name << " يجلب الكرة!" << endl;
    }
};

class Cat : public Animal {
public:
    Cat(string n, int a) : Animal(n, a) {}
    
    void speak() const override {
        cout << "مياو! 🐱" << endl;
    }
};

// Abstract class
class Shape {
public:
    virtual double area() const = 0;  // Pure virtual
    virtual double perimeter() const = 0;
    virtual void draw() const = 0;
    virtual ~Shape() {}
};

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}
    
    double area() const override {
        return 3.14159 * radius * radius;
    }
    
    double perimeter() const override {
        return 2 * 3.14159 * radius;
    }
    
    void draw() const override {
        cout << "رسم دائرة نصف قطرها " << radius << endl;
    }
};

int main() {
    Dog dog("بادي", 3, "جولدن");
    Cat cat("ميمي", 2);
    
    dog.info();
    dog.speak();
    dog.fetch();
    
    cat.info();
    cat.speak();
    
    // Polymorphism
    Animal* animals[] = {&dog, &cat};
    for (Animal* a : animals) {
        a->speak();  // يستدعي النسخة الصحيحة
    }
    
    Circle circle(5);
    cout << "\nمساحة الدائرة: " << circle.area() << endl;
    circle.draw();
    
    return 0;
}
اختبر فهمك