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