الرجوع للقائمة الرئيسية (Back to Home)

Lecture 4: Objects with Functions

المحاضرة الرابعة: الكائنات مع الدوال (Objects with Functions)

الشرح التفصيلي لجميع شرائح المحاضرة (Slide-by-Slide) مدعوم بشرح الأكواد سطراً بسطر (Line-by-Line Code Explanation)

Slide 4: تمرير الكائنات كمعاملات (Passing Objects to Functions)

Slide 4

إحنا متعودين إننا بنبعت للدوال (Functions) متغيرات عادية زي int و float. بس في الـ OOP، الكائن (Object) نفسه ممكن نعتبره Data Type ونبعته كـ Parameter لأي دالة!

عملية إرسال الـ Object دي بتتم بطريقتين أساسيتين (زي المتغيرات العادية بالظبط):

  • Pass by Value (التمرير بالقيمة): الدالة بتاخد "نسخة" (Copy) من الـ Object. أي تعديل بتعمله الدالة مش هيأثر على الـ Object الأصلي.
  • Pass by Reference (التمرير بالمرجع): الدالة بتاخد "عنوان" أو "مفتاح" الـ Object الأصلي. أي تعديل جوه الدالة هيتسمّع ويغير في الـ Object الأصلي بره.

Slide 5: التمرير بالقيمة وتأثيره (Pass by Value Deep Dive)

Slide 5

لما بنستخدم طريقة الـ Pass by Value، بيحصل حاجة خطيرة جداً وراء الكواليس (Behind the scenes) لازم تكون فاهمها:

بما إن الكمبيوتر بيعمل "نسخة" من الـ Object، فهو بيضطر يشغل الـ Copy Constructor أوتوماتيك عشان يكريت النسخة دي! (ودي موضع أسئلة امتحانات: "أي Constructor يتم استدعاؤه عند الـ Pass by Value؟").

ولما الدالة بتخلص شغلها، النسخة دي بتموت، فبالتالي الـ Destructor بتاع النسخة بيشتغل.

⚠️ ملحوظة للأداء (Performance Tip): تمرير الكائنات الكبيرة جداً بالـ Value بيستهلك مساحة ووقت (عشان بيفضل ينسخ ويهد). المبرمجين المحترفين بيفضلوا التمرير بالـ Reference عشان يوفروا الـ Memory.

Slide 6: كود التمرير بالقيمة (Pass by Value Example)

Slide 6

تعالوا نشوف إزاي نكتب دالة بتاخد Object بالـ Value:

class Distance { private: int feet; float inches; public: Distance() { feet = 0; inches = 0.0; } // Default Distance(int f, float i) { feet = f; inches = i; } // Parameterized void showDist() { cout << feet << "'-" << inches << "\""; } // Function taking an Object as Parameter! void add_dist(Distance d2, Distance d3) { inches = d2.inches + d3.inches; feet = 0; if(inches >= 12.0) { inches -= 12.0; feet++; } feet += d2.feet + d3.feet; } };

شرح الكود سطراً بسطر (Line-by-Line Analysis):

Slide 7: تشغيل الكود في الـ Main

Slide 7

في الـ Main، إزاي هنستخدم دالة الـ add_dist دي؟

int main() { Distance dist1(11, 6.25); // Call parameterized Distance dist2(5, 5.75); // Call parameterized Distance dist3; // Call default (0, 0.0) // Here is the magic: dist3.add_dist(dist1, dist2); // Print results cout << "dist1 = "; dist1.showDist(); cout << endl; cout << "dist2 = "; dist2.showDist(); cout << endl; cout << "dist3 = "; dist3.showDist(); cout << endl; return 0; }
  • عملنا مسافتين dist1 و dist2 بقيم معينة.
  • عملنا مسافة فاضية dist3 عشان نخزن فيها المجموع.
  • السطر الأهم: dist3.add_dist(dist1, dist2); : بنخلي dist3 هو اللي ينادي الدالة، ونبعتله dist1 و dist2 كـ Values يجمعهم جواه.
  • النتيجة (Output): dist3 هتبقى قيمتها (17 أقدام و 0.0 بوصة).

Quiz 1: Passing Objects

1. When passing an object to a function by VALUE, which special member function is automatically invoked by the compiler?

2. If a function modifies an object passed to it "by value", does the original object in the `main` function change?

Slide 8: التمرير بالمرجع (Pass by Reference)

Slide 8

عشان نتفادى مشكلة استهلاك الميموري وتشغيل الـ Copy Constructor اللي ملهاش لازمة، بنستخدم الـ Pass by Reference.

في الطريقة دي بنبعت للـ Function عنوان الكائن، فالدالة بتشتغل على الكائن الأصلي مباشرة! ولو خايف إن الدالة تبوظ الكائن الأصلي أو تغير بياناته (لأنها معاها المفتاح بتاعه)، الحل السحري هو إنك تستخدم كلمة const.

استخدام الـ const مع الـ Reference بيحقق حاجتين (ودي أفضل طريقة في البرمجة المتقدمة):

  1. السرعة وتوفير الذاكرة (لأنه مبينسخش حاجة).
  2. الحماية الكاملة للمتغيرات (لأن الكومبايلر هيمنع الدالة من أي عملية تعديل Read-only).

Slide 9: كود التمرير بالمرجع (Reference Example Syntax)

Slide 9

الفرق في الـ Syntax بين الـ Value والـ Reference بسيط جداً (علامة الـ &).

// Method Header with Pass by Reference void add_dist(const Distance &d2, const Distance &d3) { inches = d2.inches + d3.inches; feet = 0; if(inches >= 12.0) { inches -= 12.0; feet++; } feet += d2.feet + d3.feet; // d2.feet = 10; // ❌ ERROR: Because d2 is 'const' }
  • حطينا علامة الـ & قبل اسم المتغير (&d2). ده معناه: "يا كومبايلر، متعملش نسخة، ابعت العنوان الأصلي".
  • حطينا كلمة const في الأول. ده معناه: "اقرا الداتا بس عشان تجمعها، لكن إياك تغير قيمتها!". لو حاولت تكتب سطر زي d2.feet = 10; الكومبايلر هيشتمك ومش هيشغل البرنامج.

Slide 10: إرجاع الكائنات من الدوال (Returning Objects from Functions)

Slide 10

زي ما بنقدر "نبعت" كائن للدالة، نقدر كمان نخلي الدالة "ترجع" كائن كامل كـ Return Value.

يعني بدل ما دالة الـ add_dist تجمع النتيجة وتحطها جواها، إحنا ممكن نخليها ترجع Object جديد خالص فيه النتيجة دي، وإحنا في الـ main ناخده ونخزنه في متغير جديد.

إزاي نعمل ده؟ بنغير نوع الإرجاع (Return Type) بتاع الدالة من void لاسم الـ Class بتاعنا!

Slide 11: كود إرجاع الكائن (Return Object Example)

Slide 11

شوف التعديل في الكود عشان الدالة ترجع Object بدلاً من تعديل Object حالي:

class Distance { // ... Attributes ... public: // Notice the return type is 'Distance' not 'void' Distance add_dist(const Distance &d2) { Distance temp; // Create a temporary Object to hold the result temp.inches = inches + d2.inches; if(temp.inches >= 12.0) { temp.inches -= 12.0; temp.feet = 1; } else { temp.feet = 0; } temp.feet += feet + d2.feet; return temp; // Return the entire Object! } }; int main() { Distance d1(5, 2.0), d2(6, 11.0), d3; // Usage: d1 is calling, d2 is passed, and result is saved in d3 d3 = d1.add_dist(d2); }

شرح الكود سطراً بسطر (Line-by-Line Analysis):

Quiz 2: References & Returns

1. Why is it a "Best Practice" to use `const ClassName &obj` when passing an object to a function just to read its values?

2. To return an Object from a method, what must be the return type of that method?

Slide 19: مصفوفة الكائنات (Array of Objects)

Slide 19

تخيل إنك بتعمل برنامج لإدارة شؤون الطلبة وفيه 100 طالب. هل منطقي إنك تعمل 100 Object وتسميهم (s1, s2, s3... s100)؟ أكيد لأ!

هنا بنلجأ لمفهوم الـ Arrays (المصفوفات) اللي خدناه قبل كده، بس المرة دي هنعمل مصفوفة كائنات كاملة (Array of Objects).

ده معناه إن كل عنصر (Element) في الـ Array مش مجرد رقم ولا حرف، ده Object كامل ليه الداتا بتاعته ودواله (مثلاً طالب كامل برقمه واسمه ومجموعه)!

طريقة التعامل معاهم بتتم باستخدام الـ Index (المؤشر) اللي بيبدأ من 0 كالمعتاد، جوه حلقة تكرارية (For Loop).

Slide 20: كود مصفوفة الكائنات (Array of Objects Syntax)

Slide 20

تعالوا نشوف إزاي بنعرف الـ Array دي في الكود وإزاي بنوصل لعناصرها:

int main() { // 1. Declaration Student std_array[100]; // array of 100 Student objects // 2. Accessing and filling data for(int i = 0; i < 100; i++) { std_array[i].get_data(); // Call function for each object } // 3. Printing data for(int i = 0; i < 100; i++) { std_array[i].display_data(); } return 0; }

ملاحظات هامة:

  • لما بتكتب Student std_array[100];، الكومبايلر بيروح ينادي الـ Default Constructor 100 مرة ورا بعض! عشان يكريت الـ 100 مكان.
  • عشان نوصل לדالة معينة لكائن جوه الـ Array، بنكتب اسم الـ Array وجنبها رقم الـ Index بين أقواس مربعة، وبعدين الدوت . واسم الدالة: std_array[0].display_data();.

Slide 23: الأعضاء الثابتة (Static Class Members)

Slide 23

بشكل طبيعي، كل Object بيكون عنده نسخته الخاصة جداً من المتغيرات (يعني الطالب s1 عنده id خاص بيه، وs2 عنده id مختلف تماماً، وكل واحد في وادي).

طب لو عايزين نعمل متغير مشترك بين كل الـ Objects اللي طالعة من نفس الكلاس؟ (مثلاً عداد بيعد إحنا عملنا كام طالب لحد دلوقتي؟). المتغير العادي مش هينفع لأنه بيتمسح ويتعمل من جديد لكل كائن.

الحل هو الـ Static Data Members (المتغيرات الثابتة). بمجرد ما تحط كلمة static قبل المتغير، بيتخلق منه نسخة واحدة بس في الميموري (One Shared Copy)، وكل الـ Objects بتشارك نفس النسخة دي، وأي كائن يعدل فيها، التعديل بيسمّع عند الباقيين كلهم!

Slide 24: كود واستخدام الـ Static Member

Slide 24

إزاي نكتب المتغير الـ Static ونعمله تهيئة (Initialization) بشكل صحيح؟ (سؤال امتحان مشهور جداً):

class Student { private: // Declaration inside the class static int count; public: Student() { count++; } // Increment shared count on creation void showCount() { cout << "Total Students: " << count << endl; } }; // CRITICAL: Definition and Initialization MUST be OUTSIDE the class! int Student::count = 0; int main() { Student s1, s2, s3; s3.showCount(); // Will print: Total Students: 3 }

شرح الكود سطراً بسطر (Line-by-Line Analysis):

Slide 27: الدوال الثابتة (Static Member Functions)

Slide 27

زي ما في متغيرات Static، فيه كمان دوال (Functions) Static.

الدالة הـ Static Function مميزة جداً لسببين:

  1. عشان تنادي عليها في الـ Main، مش محتاج تعمل Object خالص! ممكن تناديها باسم الكلاس مباشرة (عن طريق ClassName::functionName()).
  2. الدالة دي ممنوع منعاً باتاً إنها تتعامل مع المتغيرات العادية (Non-static data) جوه الكلاس. هي مبتشوفش ولا بتتعامل غير مع الـ Static Variables بس!

Final Quiz: Static Members & Arrays

1. When you define an array of objects like `Student classA[50];`, which constructor is automatically called for each of the 50 elements?

2. Which of the following is a strict rule regarding `static` member functions in a class?

الملخص الشامل لأهم التريكات (Summary & Pro Tips)

سؤال مقالي متوقع للامتحان (Essay Question)

Question: Compare between "Pass by Value" and "Pass by Reference" when passing objects to functions in C++. Discuss the role of the Copy Constructor in each case, the impact on memory, and when to use `const` with references. Also, explain how `static` data members differ from regular data members.

🖥️ What is the output of the following code?

Q1:

class Counter { public: static int count; Counter() { count++; } }; int Counter::count = 0; int main() { Counter a, b, c; cout << Counter::count << endl; }

Q2:

void modify(int x) { x = x + 10; } int main() { int val = 5; modify(val); cout << val << endl; }

✍️ Write a line of code for the following statements

Q1: Declare a static data member called totalStudents of type int inside a class, then initialize it to 0 outside the class.

Q2: Write a function that takes an object of class Student by const reference and prints its name.

Q3: Create an array of 5 Student objects.

🔍 Based on the following code, answer all the following questions

class Num { private: int val; public: Num(int v) { val = v; } void add(Num &other) { val += other.val; } void show() { cout << val << endl; } }; int main() { Num n1(10), n2(20); n1.add(n2); n1.show(); n2.show(); }

a) What is the output?

b) Is n2 passed by value or by reference? What's the difference?