مقسّم حرفياً زي المنهج: شامل الـ MCQs، الأكواد، الـ Tracing، ومسائل הـ UML بالأكشن في الـ main متوثقة بالشرح العربي
Procedural programming focuses on functions rather than data, making large codebases hard to maintain.
بالعامية: البرمجة الإجرائية بتركز على الدوال، ولما البرنامج بيكبر الكود بيدخل في بعضه ويبقى Spaghetti code، عشان كدا الـ OOP ظهرت لتنظيم الكود وربط البيانات بالدوال.
OOP uses a bottom-up approach: you build the basic entities (classes) first, then use them to construct complex systems.
بالعامية: الـ OOP بتبدأ من تحت لفوق (Bottom-Up). يعني بنصمم الكلاسات والأوبجيكت الأول، وبعدين نجمعهم في الـ main عشان نبني البرنامج الكبير.
Through features like inheritance and classes, OOP promotes modular design and code reusability, saving time and reducing bugs.
بالعامية: أكبر فايدة للـ OOP إنها بتخلينا نعيد استخدام الكود (عن طريق الوراثة مثلاً) وإننا نقسم البرنامج لموديولات (كلاسات) منفصلة يسهل صيانتها (Modularity & Reusability).
Data Hiding (Encapsulation) makes it impossible for the user of the class to accidentally corrupt the internal state of the object.
By making variables private and only exposing controlled setters/getters, the internal state cannot be modified directly.
بالعامية: صح جداً. الكبسلة (Encapsulation) بتخفي البيانات (private) عشان تمنع أي حد يعدل فيها بطريقة غلط من الـ main، ولازم يستخدم دوال محددة زي الـ Setters.
Rectangle
-------------------
- width: float
- height: float
-------------------
+ setWidth(w: float): void
+ setHeight(h: float): void
+ getArea(): float
#include // استدعاء مكتبة الإدخال والإخراج
using namespace std; // استخدام الفضاء المعياري
class Rectangle { // تعريف الكلاس
private: // البيانات الخاصة (مقفول عليها)
float width; // الطول
float height; // العرض
public: // الدوال العامة اللي هنستخدمها من بره
void setWidth(float w) { width = w; } // دالة لإدخال الطول
void setHeight(float h) { height = h; } // دالة لإدخال العرض
float getArea() { // دالة لحساب المساحة
return width * height; // ضرب الطول في العرض
}
};
int main() { // الدالة الرئيسية
// Action in main!
Rectangle rect; // إنشاء أوبجيكت من نوع Rectangle
rect.setWidth(5.5); // تحديد الطول
rect.setHeight(10.0); // تحديد العرض
cout << "Area of rectangle is: " << rect.getArea() << endl; // طباعة المساحة
return 0; // إنهاء البرنامج
}
Basic encapsulation: data is private, accessed only via public setter/getter methods.
بالعامية: تطبيق أساسي على الـ Encapsulation (الكبسلة). خلينا الطول والعرض private ومحدش يقدر يوصلهم في الـ main غير من خلال الدوال الـ public زي الـ set.
Bank
-------------------
- balance: double
-------------------
+ Bank(initial: double)
+ withdraw(amount: double): bool
#include // مكتبة الطباعة
using namespace std;
class Bank { // تعريف كلاس البنك
private:
double balance; // الرصيد (مخفي)
public:
Bank(double initial) { // Constructor بيستقبل الرصيد الافتتاحي
balance = initial; // تعيين الرصيد
}
bool withdraw(double amount) { // دالة السحب
if (amount <= balance) { // لو المبلغ المطلوب أقل من أو يساوي الرصيد
balance -= amount; // اخصم المبلغ
cout << "Withdrawal successful. New balance: " << balance << endl; // طباعة نجاح
return true; // السحب تم
} else { // لو الرصيد مش مكفي
cout << "Insufficient funds!" << endl; // طباعة رسالة فشل
return false; // السحب اترفض
}
}
};
int main() {
Bank myBank(1000.0); // فتح حساب بـ 1000
myBank.withdraw(400.0); // سحب 400 (هينجح)
myBank.withdraw(800.0); // سحب 800 (هيفشل لأن الباقي 600)
return 0; // إنهاء
}
The logic checks if the requested amount is available before deduction, demonstrating how classes protect their state.
بالعامية: هنا الكلاس بيحمي البيانات بتاعته، مفيش سحب بيتم إلا لو فيه رصيد كافي. جربناها في الـ main مرة سحبنا مبلغ متاح ومرة سحبنا مبلغ أكبر عشان نتأكد إن اللوجيك شغال ومفيش رصيد بالسالب.
If you don't write `public:` or `private:`, a struct assumes everything is public, while a class assumes everything is private.
بالعامية: في الـ struct لو مكتبتش حاجة كل حاجة بتبقى مكشوفة (public). لكن الكلاس مصمم عشان يخفي البيانات فبيعتبر أي حاجة مش مكتوب قبلها modifier إنها (private).
The main reason to use Setters (Mutators) instead of making variables public is to validate data before changing it.
Setters allow us to add 'if' statements to prevent bad data (like negative age or invalid passwords) from entering the object.
بالعامية: صح جداً. الـ Setter مش مجرد دالة بتاخد قيمة تحطها في متغير وخلاص، فايدتها الأساسية إنك تقدر تحط شروط (if) جواها وتمنع المستخدم إنه يدخل داتا غلط (زي عمر بالسالب مثلاً).
If you define a parameterized constructor, the compiler still generates a default constructor for you automatically.
Once you define ANY constructor, the compiler stops generating the default one.
بالعامية: غلط. بمجرد ما بتعمل Constructor بتاعك (حتى لو بـ parameters)، الكومبايلر بيبطل يساعدك ولازم تكتب الـ Default بنفسك لو عاوزه.
A copy constructor is triggered whenever a completely new object is created from an existing object.
بالعامية: الـ Copy Constructor بيشتغل في كل الحالات دي: لو رجعت أوبجيكت By Value من دالة، أو بعته لدالة By Value، أو لو ساويت أوبجيكت جديد بواحد قديم وقت تعريفه (زي Student s2 = s1).
class Test {
public:
Test() { cout << "D "; }
Test(int x) { cout << "P "; }
};
int main() {
Test t1;
Test t2(5);
Test t3();
return 0;
}
Test t3() is a function declaration (returning a Test object), NOT an object instantiation! It is known as the Most Vexing Parse in C++.
بالعامية: خبيثة جداً جداً! السطر الأول بيطبع D، التاني بيطبع P. السطر التالت `Test t3()` ده مش بيعمل أوبجيكت! الكومبايلر بيفتكره دالة عادية اسمها t3 بترجع أوبجيكت من نوع Test، فمش بيستدعي الـ Constructor خالص.
Student
-------------------
- id: int
- name: string
-------------------
+ Student()
+ Student(i: int, n: string)
+ Student(const s: Student&)
+ ~Student()
+ print(): void
#include
#include // مكتبة النصوص
using namespace std;
class Student { // كلاس الطالب
private:
int id; // رقم الطالب
string name; // اسم الطالب
public:
// 1. Default Constructor
Student() { // الـ Constructor الافتراضي (فاضي)
id = 0; name = "Unknown"; // قيم ابتدائية
cout << "Default Constructor Called\n"; // للتأكيد
}
// 2. Parameterized Constructor
Student(int i, string n) { // بياخد بارامترز
id = i; name = n; // تعيين القيم
cout << "Parameterized Constructor Called\n";
}
// 3. Copy Constructor
Student(const Student& s) { // بياخد Reference من أوبجيكت تاني
id = s.id; // نسخ الـ ID
name = s.name; // نسخ الاسم
cout << "Copy Constructor Called\n";
}
// Destructor
~Student() { // الهدّام (بيشتغل لما الأوبجيكت يموت)
cout << "Destructor Called for " << name << "\n";
}
void print() { cout << "ID: " << id << " Name: " << name << endl; } // دالة الطباعة
};
int main() {
Student s1; // أوبجيكت فاضي (ينادي Default)
Student s2(101, "Ahmed"); // أوبجيكت ببيانات (ينادي Parameterized)
Student s3 = s2; // أوبجيكت جديد بينسخ من s2 (ينادي Copy)
s3.print(); // طباعة بيانات s3
return 0; // البرنامج بيقفل والهدام بيشتغل بالعكس (s3 ثم s2 ثم s1)
}
Demonstrates the complete lifecycle of objects. Notice the Copy constructor must take a reference.
بالعامية: المسألة دي بتلم كل أنواع الـ Constructors. في الـ main جربنا نعمل أوبجيكت فاضي (Default)، وأوبجيكت ببيانات (Parameterized)، وأوبجيكت بينسخ من واحد تاني (Copy). وفي النهاية הـ Destructor هيمسحهم بالعكس.
Memory is allocated for a Class when it is defined in the code.
A class is just a blueprint. NO memory is allocated until an object of that class is created.
بالعامية: غلط! الكلاس مجرد تصميم على ورق (Blueprint) مابياخدش أي ميموري خالص. الميموري بتتحجز أول ما تصنع منه أوبجيكت (Object Creation) في الـ main.
Passing by value makes a full copy of the object which is slow and consumes memory. Passing by reference just passes the memory address.
بالعامية: تمرير الـ Object للدوال (By Value) بيخلي الكومبايلر يشغل הـ Copy Constructor ويعمل نسخة كاملة من الأوبجيكت بكل اللي فيه وده بيبطئ البرنامج. عشان كده دايماً بنبعته (By Reference &) عشان نبعت مساره بس، وبنحط const لو مش عايزين الدالة تغير فيه.
An inline function guarantees that the compiler will expand it at the call site.
Inline is only a REQUEST to the compiler. If the function is too complex, contains loops, or is recursive, the compiler ignores the inline request.
بالعامية: غلط. كلمة Inline دي (طلب) بنطلبه من الكومبايلر، بس لو الدالة كانت معقدة أوي أو فيها Loops الكومبايلر هيرفض الطلب ويعتبرها دالة عادية جداً.
class Number {
int val;
public:
Number(int v) { val = v; }
int getVal() { return val; } // NOT const
};
int main() {
const Number n(10);
cout << n.getVal();
return 0;
}
A constant object (`const Number n`) CANNOT call non-const methods. Since `getVal` is not marked as const, the compiler blocks it to protect the object.
بالعامية: عشان الأوبجيكت `n` متعرف إنه ثابت (const)، الكومبايلر بيمنعه إنه يستدعي أي دالة مش متأمنة بكلمة const، عشان خايف إنها تغير قيمته. فلازم نكتب `int getVal() const` عشان يشتغل.
Static methods belong to the class level. They do not know about specific objects, so they have no 'this' pointer and can only interact with static variables.
بالعامية: الدوال הـ Static دي بتاعة الكلاس ككل مش بتاعة أوبجيكت معين. فمتقدرش توصل لمتغيرات عادية (لأن مفيش this pointer يعرّفها الأوبجيكت فين)، وتتعامل بس مع الحاجات הـ static زيها.
Point
-------------------
- x: int
- y: int
-------------------
+ Point(x: int, y: int)
+ inline print() const: void
+ addPoints(p1: Point, p2: Point): Point
#include
using namespace std;
class Point { // كلاس النقطة
private:
int x, y; // الإحداثيات
public:
Point(int xVal, int yVal) { // Constructor
x = xVal; y = yVal;
}
// Inline & Const function
inline void print() const { // دالة Inline وسريعة وكمان Const
cout << "(" << x << ", " << y << ")\n"; // طباعة النقطة
}
// Function taking objects and returning an object
Point addPoints(Point p1, Point p2) { // دالة بتاخد 2 أوبجيكتس
Point temp(p1.x + p2.x, p1.y + p2.y); // بنجمعهم ونحطهم في أوبجيكت جديد
return temp; // بنرجع الأوبجيكت الجديد
}
};
int main() {
Point p1(3, 4); // النقطة الأولى
Point p2(1, 2); // النقطة التانية
// Note: Calling via object since it's a member function
Point p3 = p1.addPoints(p1, p2); // بننادي الدالة عشان تجمعهم
cout << "Result: ";
p3.print(); // بنطبع النتيجة
return 0; // إنهاء
}
Objects can be passed exactly like normal variables, and returned identically. Const methods guarantee the object's data won't change.
بالعامية: في الـ main بعتنا اتنين أوبجيكتس للدالة `addPoints`، الدالة جمعت الـ X والـ Y وعملت أوبجيكت جديد (temp) ورجعته، واستقبلناه في `p3`. واستخدمنا `inline` عشان نسرع أداء الطباعة.
Car
-------------------
- model: string
- static totalCars: int
-------------------
+ Car()
+ Car(m: string)
+ static getTotalCars(): int
+ ~Car()
#include
#include
using namespace std;
class Car { // كلاس العربية
private:
string model; // موديل العربية
static int totalCars; // متغير Static (مُشترك بين كل العربيات)
public:
Car() { // Default Constructor
model = "Default";
totalCars++; // بنزود عدد العربيات
}
Car(string m) { // Parameterized Constructor
model = m;
totalCars++; // بنزود عدد العربيات
}
static int getTotalCars() { // دالة Static عشان ترجع العدد
return totalCars; // هترجع المتغير الـ static
}
~Car() { // Destructor
totalCars--; // لما عربية تتمسح، ننقص العدد
}
};
// Static Initialization MUST be outside the class!
int Car::totalCars = 0; // تعريف المتغير הـ Static بره الكلاس ونديله صفر
int main() {
cout << "Initial Cars: " << Car::getTotalCars() << endl; // بنشوف العدد قبل أي حاجة
// Array of Objects calls Default Constructor 3 times
Car showroom[3]; // حجزنا مصفوفة فيها 3 عربيات
cout << "Cars after array creation: " << Car::getTotalCars() << endl; // العدد بقى 3
{
Car temp("BMW"); // عملنا عربية جديدة جوه قوسين Scope
cout << "Cars inside block: " << Car::getTotalCars() << endl; // العدد بقى 4
} // الـ Scope خلص، عربية BMW اتمسحت، الهدام نقص العدد
cout << "Cars after block: " << Car::getTotalCars() << endl; // العدد رجع 3 تاني
return 0; // إنهاء
}
Static variables are shared among all instances. They must be initialized globally outside the class.
بالعامية: المتغير الـ static مشترك بين كل العربيات. أول ما عملنا Array فيها 3 عربيات، الـ Constructor اشتغل 3 مرات وزود العدد. ولما الأوبجيكت اللي جوه القوسين مات، الـ Destructor اشتغل ونقص العدد. وطبعنا العدد بدون ما نحتاج أوبجيكت عن طريق `Car::getTotalCars()`.
In OOP, objects must be initialized. `new` ensures the constructor is called automatically after memory is allocated.
بالعامية: دي حتة مهمة جداً! `new` في الـ C++ مش بس بتحجز مكان في الـ Heap، دي كمان بتشغل الـ Constructor عشان تبني الأوبجيكت صح. لكن `malloc` بتاعة الـ C كانت بتحجز ميموري فاضية (زبالة) ومش بتشغل حاجة.
The stack has limited size. If a function calls itself infinitely, it fills up the stack memory until it overflows.
بالعامية: الـ Stack مساحته صغيرة نسبياً. لو عملت دالة بتنادي نفسها إلى ما لا نهاية (Recursion بدون شرط توقف) أو عملت مصفوفة حجمها ضخم جداً جوه دالة عادية، الـ Stack بيتملي ويضرب ويديك Error.
A Memory Leak happens when a pointer pointing to dynamically allocated memory goes out of scope without calling `delete`.
If the pointer disappears, the memory it was pointing to is orphaned and can never be freed.
بالعامية: صح تماماً. لو البوينتر اتمسح من الـ Stack لأنه خلص الـ Scope، الميموري اللي حجزها في הـ Heap هتفضل محجوزة على الفاضي ومفيش أي طريقة نوصلها بيها. وده هو الـ Memory Leak.
void process() {
int* data = new int[500];
// processing ...
data = new int[1000];
delete[] data;
}
The pointer `data` originally pointed to 500 ints. By reassigning it without `delete[]` first, those 500 ints are lost in the Heap forever.
بالعامية: حجزنا 500 مكان، وبعدين البوينتر سابهم ومسك في 1000 مكان تانيين! طب الـ 500 الأوليين دول هيمسحهم إزاي ومفيش بوينتر بيشاور عليهم؟ ضاعوا في الميموري (Memory Leak).
DynamicArray
-------------------
- size: int
- arr: int*
-------------------
+ DynamicArray(s: int)
+ fillData(): void
+ ~DynamicArray()
#include
using namespace std;
class DynamicArray { // الكلاس
private:
int size; // حجم المصفوفة
int* arr; // بوينتر هيشاور على المصفوفة في الـ Heap
public:
DynamicArray(int s) { // Constructor
size = s; // نسجل الحجم
arr = new int[size]; // بنحجز الميموري في הـ Heap
cout << "Array of size " << size << " created in Heap.\n";
}
void fillData() { // دالة تعبئة البيانات
for(int i=0; i// Loop لملء الأرقام
arr[i] = i * 10; // بنملا المصفوفة
}
cout << "First element: " << arr[0] << ", Last: " << arr[size-1] << endl;
}
~DynamicArray() { // Destructor الأهم
delete[] arr; // لازم نمسح المصفوفة عشان نمنع الـ Memory Leak!
cout << "Array memory freed.\n";
}
};
int main() {
// Dynamically allocating the OBJECT itself in the Heap!
DynamicArray* myArr = new DynamicArray(50); // بنحجز الأوبجيكت نفسه في الـ Heap بـ new
myArr->fillData(); // بننادي دالة التعبئة
// If we don't call delete, the destructor is NEVER called!
delete myArr; // خطوة قاتلة: لازم نعمل delete عشان הـ Destructor يشتغل والمصفوفة تتمسح
return 0; // إنهاء
}
A dynamic object must be explicitly deleted. When `delete myArr` is called, the object's destructor runs, which in turn deletes the inner dynamic array.
بالعامية: هنا إحنا حجزنا (أوبجيكت) كامل في الـ Heap باستخدام new. الأوبجيكت ده جواه Constructor بيحجز (مصفوفة) في الـ Heap بردو! لو معملناش `delete myArr` في الـ main، الهدّام مش هيشتغل والمصفوفة مش هتتمسح والدنيا هتبوظ.
StringBox
-------------------
- text: char*
-------------------
+ StringBox(t: const char*)
+ StringBox(const other: StringBox&) << Deep Copy
+ ~StringBox()
+ print(): void
#include
#include // مكتبة التعامل مع الحروف
using namespace std;
class StringBox { // كلاس الصندوق
private:
char* text; // بوينتر لكلمة
public:
// Regular Constructor
StringBox(const char* t) {
text = new char[strlen(t) + 1]; // بنحجز مكان على قد الكلمة بالضبط في הـ Heap
strcpy(text, t); // بننسخ الكلمة في الميموري
}
// Deep Copy Constructor!
StringBox(const StringBox& other) { // الـ Copy Constructor الحقيقي عشان يحل مشكلة الـ Shallow
text = new char[strlen(other.text) + 1]; // بنحجز مكان جديد خالص (NEW memory)
strcpy(text, other.text); // بننسخ الداتا نفسها مش مجرد الـ Address
cout << "Deep copy performed.\n";
}
~StringBox() { // الهدام
delete[] text; // بيمسح الميموري بتاعته بس (بدون تدخل في الميموري بتاعة الأوبجيكتس التانية)
}
void print() { cout << "Text: " << text << endl; } // الطباعة
};
int main() {
StringBox box1("Hello OOP"); // الأوبجيكت الأول
// This calls the copy constructor. If it was shallow, both would point to same memory!
StringBox box2 = box1; // الأوبجيكت التاني بيعمل Deep Copy من الأول
box1.print();
box2.print();
// لما الـ main يخلص، الاتنين Destructors هيشتغلوا ويمسحوا أماكن منفصلة بسلام.
return 0;
}
Without a deep copy constructor, both objects' pointers would hold the same memory address. Deleting one would make the other a dangling pointer, crashing the program when it's deleted twice.
بالعامية: لو معملناش Copy Constructor بـ Deep Copy، الكومبايلر هينسخ عنوان البوينتر بس! ولما البرنامج يخلص، الاتنين Destructors هيحاولوا يمسحوا نفس العنوان من الـ Heap والبرنامج هيضرب (Double Free). هنا إحنا حجزنا مكان جديد خالص للنسخة ونقلنا فيه الداتا.
Hybrid Inheritance is a combination of more than one type of inheritance (e.g., combining Multilevel and Multiple inheritance).
Yes, whenever a class structure mixes different inheritance models (like a diamond shape), it is termed hybrid inheritance.
بالعامية: صح. الوراثة الهجينة (Hybrid) هي لما البرنامج بتاعنا يبقى فيه خلطة من الوراثة، يعني مثلاً فيه جزء Multi-level شغال وجزء Multiple، فبنسميها مهجنة.
In private inheritance, the derived class can use the base methods internally, but they are completely hidden from the outside world (the main).
بالعامية: لما الكلاس الابن يورث بشكل `private`، بياخد كل حاجة `public` و `protected` من الأب يحبسها جواه ويخليها `private` عنده هو، وبالتالي محدش في הـ main بيقدر يشوفها.
In inheritance, the derived class constructor runs BEFORE the base class constructor.
The Base class (the foundation) is always constructed BEFORE the Derived class.
بالعامية: غلط تماماً. البناء بيبدأ من الأب (الأساس) وبعدين الابن بيكمل عليه. الهدد بس هو اللي بيبدأ بالابن الأول.
class A {
public: A() { cout << "1"; } ~A() { cout << "2"; }
};
class B : public A {
public: B() { cout << "3"; } ~B() { cout << "4"; }
};
int main() { B obj; }
Constructors: Base(1) -> Derived(3). Destructors: Derived(4) -> Base(2).
بالعامية: الأساس بيتبني الأول (A هيطبع 1)، بعدين الابن (B هيطبع 3). الهدد بيبقى العكس، نهدم الدور الأخير (B هيطبع 4)، وبعدين الأساس (A هيطبع 2). يبقى الناتج 1 3 4 2.
Engine Wheels
------- -------
- hp: int - size: int
------- -------
+ Engine(h: int) + Wheels(s: int)
+ showHp() + showSize()
\ /
\ /
\ /
Car (public Engine, public Wheels)
-------------------
- brand: string
-------------------
+ Car(h: int, s: int, b: string)
+ displayInfo(): void
#include
#include
using namespace std;
class Engine { // الأب الأول (المحرك)
protected:
int hp; // قوة الحصان
public:
Engine(int h) { hp = h; } // Constructor بتاع المحرك
void showHp() { cout << "Engine HP: " << hp << endl; } // طباعة القوة
};
class Wheels { // الأب التاني (العجلات)
protected:
int size; // مقاس العجلة
public:
Wheels(int s) { size = s; } // Constructor بتاع العجل
void showSize() { cout << "Wheel Size: " << size << " inches" << endl; } // طباعة المقاس
};
// Multiple Inheritance! Car inherits from BOTH Engine and Wheels
class Car : public Engine, public Wheels { // وراثة متعددة، العربية بتورثهم الاتنين
private:
string brand; // ماركة العربية
public:
// MUST call both base constructors in the initialization list!
Car(int h, int s, string b) : Engine(h), Wheels(s) { // بندي الآباء القيم بتاعتهم هنا قبل أي حاجة
brand = b; // بنخزن המاركة
}
void displayInfo() { // دالة بنجمع فيها الداتا
cout << "Brand: " << brand << endl; // بنطبع الماركة
showHp(); // الدالة دي جيالنا من الأب الأول (المحرك)
showSize(); // الدالة دي جيالنا من الأب التاني (العجل)
}
};
int main() {
Car myCar(500, 19, "Ferrari"); // بنعمل عربية فيراري بقوة 500 وعجل 19
myCar.displayInfo(); // بنعرض كل الداتا اللي اتجمعت من الوراثة
return 0; // إنهاء
}
In Multiple Inheritance, the child has multiple parents. It must initialize all of them in its constructor's initializer list.
بالعامية: ده تطبيق على الوراثة المتعددة (Multiple Inheritance). العربية بتورث من محرك ومن عجلات. ففي Constructor بتاع العربية، لازم ننادي على Constructor الأب الأول والأب التاني في הـ Initialization List عشان يتملوا بيانات.
Person
-------------------
# name: string
-------------------
+ Person(n: string)
^
|
Employee
-------------------
# id: int
-------------------
+ Employee(n: string, i: int)
^
|
Manager
-------------------
- bonus: double
-------------------
+ Manager(n: string, i: int, b: double)
+ display(): void
#include
#include
using namespace std;
class Person { // الجد
protected:
string name; // اسم الشخص
public:
Person(string n) { name = n; } // الجد بياخد الاسم
};
class Employee : public Person { // الأب بيورث الجد
protected:
int id; // رقم الموظف
public:
// Pass 'n' up to Person
Employee(string n, int i) : Person(n) { // بنسلم הـ name للجد في الأقواس بتاعت الـ Initialization
id = i; // بنسجل הـ id
}
};
class Manager : public Employee { // الابن بيورث الأب (كده بقى عندنا 3 مستويات)
private:
double bonus; // البونص
public:
// Pass 'n' and 'i' up to Employee
Manager(string n, int i, double b) : Employee(n, i) { // بنسلم الداتا للأب
bonus = b; // بنسجل الـ bonus
}
void display() {
// Can access protected members from grandparents directly!
cout << "Manager: " << name << ", ID: " << id << ", Bonus: $" << bonus << endl; // الـ name ده جاي من الجد مباشرة!
}
};
int main() {
Manager boss("Ahmed", 101, 5000.0); // عملنا المدير وبعتناله بيانات الـ 3 مستويات
boss.display(); // طبعنا البيانات الموروثة
return 0; // إنهاء
}
Constructor chaining is required in multi-level inheritance to pass arguments up the hierarchy.
بالعامية: الوراثة المتعددة المستويات (جد -> أب -> ابن). كل كلاس لازم يسلم المتغيرات للي فوقه في الـ Initialization List. ولأن المتغيرات `protected`، الحفيد قدر يستخدم `name` بتاع الجد مباشرة من غير Getter.
Vehicle
# speed: int
+ Vehicle(s)
/ \
/ \
Car Truck
- doors: int - loadCapacity: int
+ Car(s, d) + Truck(s, l)
+ show() + show()
#include
using namespace std;
class Vehicle { // الأب
protected:
int speed; // السرعة (موروثة للأبناء)
public:
Vehicle(int s) { speed = s; } // Constructor الأب
};
class Car : public Vehicle { // الابن الأول (عربية)
private:
int doors; // الأبواب
public:
Car(int s, int d) : Vehicle(s) { doors = d; } // بياخد السرعة يديها للأب
void show() { cout << "Car Speed: " << speed << ", Doors: " << doors << endl; } // طباعة الداتا
};
class Truck : public Vehicle { // الابن التاني (شاحنة)
private:
int loadCapacity; // الحمولة
public:
Truck(int s, int l) : Vehicle(s) { loadCapacity = l; } // بياخد السرعة يديها للأب
void show() { cout << "Truck Speed: " << speed << ", Load: " << loadCapacity << " Tons" << endl; } // طباعة الداتا
};
int main() {
Car c(120, 4); // عملنا عربية
Truck t(80, 10); // عملنا شاحنة
c.show(); // طبعنا العربية اللي ورثت الـ speed من الأب
t.show(); // طبعنا الشاحنة اللي كمان ورثت الـ speed من الأب
return 0; // إنهاء
}
In hierarchical inheritance, multiple distinct child classes inherit from one single parent class.
بالعامية: الوراثة الهرمية، أب واحد (Vehicle) طالع منه كذا ابن (Car و Truck). كل ابن بيبعت الـ speed للأب عشان يتسجل عنده ويكمل بناء نفسه ببياناته الخاصة.
A pure virtual function explicitly marks the class as a base-only concept, preventing instantiation.
بالعامية: عشان الكلاس يبقى Abstract ومحدش يقدر يعمل منه Object خالص، لازم يكون جواه دالة `virtual` ومكتوب جنبها `= 0` (Pure Virtual).
A virtual destructor is important when you delete a derived object through a base class pointer.
If the base destructor is not virtual, deleting via a base pointer will only call the base destructor, leaking derived class memory.
بالعامية: صح جداً ومهمة جداً. لو البوينتر نوعه (أب) وبيشاور على (ابن)، ولما تيجي تمسحه بـ delete لو الهدّام مش virtual، الكومبايلر هيمسح الأب بس ويسيب الابن يعمل Memory Leak!
class Base {
public: virtual void print() { cout << "Base"; }
};
class Derived : public Base {
public: void print() { cout << "Derived"; }
};
int main() {
Base* ptr = new Derived();
ptr->print();
}
The `virtual` keyword tells the compiler to check the actual object type at run-time (Late Binding), resolving to Derived.
بالعامية: بسبب كلمة `virtual` اللي في دالة الأب، الكومبايلر بيعمل Late Binding وبيستنى وقت التشغيل يشوف البوينتر بيشاور على أوبجيكت إيه بالظبط (بيشاور على Derived)، فبيطبع Derived.
Animal (Abstract)
-------------------
-------------------
+ virtual sound() = 0: void
^
_____|_____
| |
Dog Cat
------- -------
------- -------
+ sound() + sound()
#include
using namespace std;
// Abstract Class (عشان فيه Pure Virtual)
class Animal {
public:
virtual void sound() = 0; // Pure Virtual Function (مفيش جسم للدالة)
virtual ~Animal() {} // Virtual Destructor (أساسي عشان الـ Memory)
};
class Dog : public Animal { // الكلب بيورث من الحيوان
public:
void sound() override { cout << "Dog says: Woof!\n"; } // لازم يطبق الدالة بتاعة الصوت
};
class Cat : public Animal { // القطة بتورث من الحيوان
public:
void sound() override { cout << "Cat says: Meow!\n"; } // لازم تطبق الدالة بردو
};
int main() {
// Array of Base Class Pointers (Polymorphism Magic!)
Animal* zoo[2]; // مصفوفة بوينترات من نوع الأب
zoo[0] = new Dog(); // الأول بيشاور على كلب
zoo[1] = new Cat(); // التاني بيشاور على قطة
cout << "--- Zoo Sounds ---\n";
for(int i = 0; i < 2; i++) { // Loop للتشغيل
// Late Binding happens here based on the actual object!
zoo[i]->sound(); // الكومبايلر بيستنى وقت التشغيل وينادي الصوت الصح حسب الأوبجيكت الحقيقي!
}
// Cleanup
for(int i = 0; i < 2; i++) { // بنلف عليهم نمسحهم
delete zoo[i]; // بنمسحهم عشان الـ Memory Leak، وهنا الـ Virtual Destructor هيشتغل
}
return 0; // إنهاء
}
An array of base pointers iterating over derived objects is the classic, most powerful use-case of Polymorphism.
بالعامية: ده أعظم مثال يوضح الـ Polymorphism (تعدد الأوجه). عملنا مصفوفة بوينترات من نوع الأب Animal، وحطينا جواها كلب وقطة. ولما عملنا Loop نادينا دالة `sound()` واحدة بس، بس كل حيوان طلع الصوت بتاعه حسب نوعه الحقيقي (Late Binding).
MathBase
-------------------
-------------------
+ virtual compute(x: int): void
+ compute(x: double): void
^
|
MathDerived
-------------------
-------------------
+ compute(x: int) override: void
#include
using namespace std;
class MathBase { // الأب
public:
// Virtual function ready for Overriding
virtual void compute(int x) { // دالة بتاخد رقم صحيح
cout << "Base compute(int): " << x * 2 << endl;
}
// Overloaded function (same name, different parameter)
void compute(double x) { // نفس الاسم بس بتاخد كسر (ده اسمه Overloading جوه الكلاس)
cout << "Base compute(double): " << x * 3.14 << endl;
}
};
class MathDerived : public MathBase { // الابن
public:
// Overriding the exact same signature
void compute(int x) override { // كتبنا نفس الدالة بالظبط تاني عشان نعدلها (ده الـ Overriding مع الوراثة)
cout << "Derived compute(int) overridden!: " << x * x << endl;
}
// Note: C++ hides overloaded base functions if a derived class overrides one of them
// To unhide them, we use: using MathBase::compute;
using MathBase::compute; // حيلة مهمة جداً عشان الدالة التانية بتاعة الكسر متستخباش
};
int main() {
MathDerived obj; // عملنا الأوبجيكت من نوع الابن
// Calls overridden Derived method
obj.compute(5); // بينادي الدالة اللي تعدلت
// Calls overloaded Base method (only works because of 'using' keyword)
obj.compute(5.5); // بينادي دالة الكسر اللي جايالنا من الأب
// Polymorphism test
MathBase* ptr = &obj; // بوينتر من نوع الأب بيشاور عالابن
ptr->compute(10); // Late binding -> calls Derived (بينادي الابن عشان virtual)
return 0; // إنهاء
}
Demonstrates the subtle difference: Overloading is within the same scope with different parameters. Overriding is across inheritance scopes with the EXACT same parameters.
بالعامية: هنا بنشرح الفرق بين الـ Overloading (نفس الاسم بس بياخد داتا مختلفة جوه نفس الكلاس) والـ Overriding (الابن بيورث دالة من الأب وبيكتبها تاني بنفس كل حاجة عشان يعدلها). وفي הـ main جربنا الاتنين وجربنا הـ Polymorphism.