C++ تعدد الأشكال مع المثال
ما هو تعدد الأشكال في C++?
In C++يؤدي تعدد الأشكال إلى سلوك وظيفة العضو بشكل مختلف بناءً على الكائن الذي يستدعيها/يستدعيها. تعدد الأشكال هي كلمة يونانية تعني أن يكون هناك أشكال عديدة. يحدث ذلك عندما يكون لديك تسلسل هرمي للفئات المرتبطة بالميراث.
على سبيل المثال، لنفترض أن لدينا الدالة makeSound(). عندما تستدعي القطة هذه الوظيفة، فإنها ستنتج صوت المواء. عندما تستدعي البقرة نفس الوظيفة، فإنها ستصدر صوت المواء.
على الرغم من أن لدينا وظيفة واحدة، إلا أنها تتصرف بشكل مختلف في ظل ظروف مختلفة. الدالة لها أشكال عديدة؛ وبالتالي، حققنا تعدد الأشكال.
أنواع تعدد الأشكال
C++ يدعم نوعين من تعدد الأشكال:
- تعدد الأشكال في وقت الترجمة، و
- تعدد الأشكال في وقت التشغيل.
تجميع تعدد الأشكال الوقت
يمكنك استدعاء الوظائف المحملة بشكل زائد عن طريق مطابقة عدد ونوع الوسائط. المعلومات موجودة أثناء وقت الترجمة. وهذا يعني C++ سيختار المترجم الوظيفة المناسبة في وقت الترجمة.
يتم تحقيق تعدد الأشكال في وقت التجميع من خلال التحميل الزائد للوظيفة والتحميل الزائد للمشغل.
وظيفة التحميل الزائد
يحدث التحميل الزائد للوظائف عندما يكون لدينا العديد من الوظائف بأسماء متشابهة ولكن وسائط مختلفة. قد تختلف الوسائط من حيث العدد أو النوع.
مثال 1
#include <iostream> using namespace std; void test(int i) { cout << " The int is " << i << endl; } void test(double f) { cout << " The float is " << f << endl; } void test(char const *ch) { cout << " The char* is " << ch << endl; } int main() { test(5); test(5.5); test("five"); return 0; }
الإخراج:
فيما يلي لقطة شاشة للكود:
شرح الكود:
- قم بتضمين ملف رأس iostream في الكود الخاص بنا. سنكون قادرين على استخدام وظائفها.
- قم بتضمين مساحة الاسم std في الكود الخاص بنا. سنكون قادرين على استخدام فئاته دون الاتصال به.
- قم بإنشاء دالة باسم test تأخذ معلمة عدد صحيح i. يمثل { بداية نص اختبار الوظيفة.
- البيان الذي سيتم تنفيذه إذا تم استدعاء/استدعاء اختبار الوظيفة أعلاه.
- نهاية جسم اختبار الوظيفة أعلاه.
- قم بإنشاء دالة باسم test تأخذ معلمة عائمة f. يمثل { بداية نص اختبار الوظيفة.
- البيان الذي سيتم تنفيذه إذا تم استدعاء/استدعاء اختبار الوظيفة أعلاه.
- نهاية نص اختبار الوظيفة أعلاه.
- قم بإنشاء دالة باسم test تأخذ معلمة حرف ch. يمثل { بداية نص اختبار الوظيفة.
- البيان الذي سيتم تنفيذه إذا تم استدعاء/استدعاء اختبار الوظيفة أعلاه.
- نهاية نص اختبار الوظيفة أعلاه.
- استدعاء الدالة الرئيسية (). يمثل { بداية نص الوظيفة.
- استدعاء اختبار الدالة وتمرير 5 إليه كقيمة الوسيطة. يؤدي هذا إلى استدعاء دالة الاختبار التي تقبل وسيطة عدد صحيح، أي دالة الاختبار الأولى.
- استدعاء اختبار الدالة وتمرير 5.5 إليه كقيمة الوسيطة. سيؤدي هذا إلى استدعاء وظيفة الاختبار التي تقبل وسيطة عائمة، أي وظيفة الاختبار الثانية.
- استدعاء اختبار الدالة وتمرير خمسة إليها كقيمة الوسيطة. سيؤدي هذا إلى استدعاء وظيفة الاختبار التي تقبل وسيطة الحرف، وهي وظيفة الاختبار الثالثة.
- يجب أن يقوم البرنامج بإرجاع قيمة إذا تم تشغيله بنجاح.
- نهاية نص الدالة main().
لدينا ثلاث وظائف بنفس الاسم ولكن أنواع مختلفة من الوسائط. لقد حققنا تعدد الأشكال.
Operaالحمولة الزائدة
In Operaالتحميل الزائد لـ Tor، نحدد معنى جديدًا لـ a C++ عامل. كما أنه يغير كيفية عمل العامل. على سبيل المثال، يمكننا تعريف عامل + لربط سلسلتين. نعرفه كعامل الجمع لإضافة قيم عددية. بعد تعريفنا، عند وضعه بين الأعداد الصحيحة، فإنه سيضيفها. عند وضعه بين السلاسل، فإنه سيربطها.
مثال 2
#include<iostream> using namespace std; class ComplexNum { private: int real, over; public: ComplexNum(int rl = 0, int ov = 0) { real = rl; over = ov; } ComplexNum operator + (ComplexNum const &obj) { ComplexNum result; result.real = real + obj.real; result.over = over + obj.over; return result; } void print() { cout << real << " + i" << over << endl; } }; int main() { ComplexNum c1(10, 2), c2(3, 7); ComplexNum c3 = c1+c2; c3.print(); }
الإخراج:
فيما يلي لقطة شاشة للكود:
شرح الكود:
- قم بتضمين ملف رأس iostream في برنامجنا لاستخدام وظائفه.
- قم بتضمين مساحة الاسم std في برنامجنا لاستخدام فئاته دون استدعائها.
- قم بإنشاء فئة باسم ComplexNum. يشير الرمز { إلى بداية نص الفئة.
- استخدم معدل الوصول الخاص لوضع علامة على المتغيرات على أنها خاصة، مما يعني أنه لا يمكن الوصول إليها إلا من داخل الفصل الدراسي.
- حدد متغيرين صحيحين، حقيقي وأكثر.
- استخدم معدّل الوصول العام لوضع علامة على المُنشئ باعتباره عامًا، مما يعني أنه سيكون من الممكن الوصول إليه حتى من خارج فئة.
- إنشاء منشئ الفئة وتهيئة المتغيرات.
- تهيئة قيمة المتغير الحقيقي.
- تهيئة قيمة المتغير انتهى.
- نهاية الجسم المنشئ.
- نحن بحاجة إلى تجاوز معنى عامل +.
- إنشاء نتيجة نوع البيانات من نوع ComplexNum.
- استخدم عامل + مع الأعداد المركبة. سيضيف هذا السطر الجزء الحقيقي من رقم إلى الجزء الحقيقي من رقم آخر.
- استخدم عامل + مع الأعداد المركبة. سيضيف هذا السطر الجزء التخيلي من رقم إلى الجزء التخيلي من رقم آخر.
- سيقوم البرنامج بإرجاع قيمة النتيجة المتغيرة عند التنفيذ الناجح.
- نهاية تعريف المعنى الجديد لعامل +، أي التحميل الزائد.
- استدعاء طريقة الطباعة ().
- اطبع العدد المركب الجديد بعد الجمع على وحدة التحكم.
- نهاية نص الدالة print().
- نهاية نص فئة ComplexNum.
- استدعاء الدالة الرئيسية ().
- مرر قيم الأجزاء الحقيقية والمركبة المراد إضافتها. سيتم إضافة الجزء الأول من c1 إلى الجزء الأول من c2، أي 10+3. سيتم إضافة الجزء الثاني من c1 إلى الجزء الثاني من c، أي 2+7.
- قم بإجراء عملية باستخدام عامل + المحمل وتخزين النتيجة في المتغير c3.
- اطبع قيمة المتغير c3 على وحدة التحكم.
- نهاية نص الدالة main().
تعدد الأشكال وقت التشغيل
يحدث هذا عندما يتم استدعاء/استدعاء أسلوب الكائن أثناء وقت التشغيل وليس أثناء وقت الترجمة. يتم تحقيق تعدد الأشكال في وقت التشغيل من خلال تجاوز الوظيفة. يتم إنشاء الوظيفة التي سيتم استدعاؤها/استدعاؤها أثناء وقت التشغيل.
تجاوز الوظيفة
يحدث تجاوز الوظيفة عندما يتم إعطاء دالة من الفئة الأساسية تعريفًا جديدًا في فئة مشتقة. في ذلك الوقت، يمكننا القول أن الوظيفة الأساسية قد تم تجاوزها.
فمثلا:
#include <iostream> using namespace std; class Mammal { public: void eat() { cout << "Mammals eat..."; } }; class Cow: public Mammal { public: void eat() { cout << "Cows eat grass..."; } }; int main(void) { Cow c = Cow(); c.eat(); return 0; }
الإخراج:
فيما يلي لقطة شاشة للكود:
شرح الكود:
- قم باستيراد ملف رأس iostream إلى برنامجنا لاستخدام وظائفه.
- قم بتضمين مساحة الاسم std في برنامجنا لاستخدام فئاته دون استدعائها.
- قم بإنشاء فئة تسمى الثدييات. يمثل { بداية نص الفصل.
- استخدم معدّل الوصول العام لتعيين الوظيفة التي نحن على وشك إنشائها لتكون متاحة للعامة. سيكون متاحًا من خارج هذا الفصل.
- قم بإنشاء وظيفة عامة باسم Eat. يمثل { بداية نص الوظيفة.
- اطبع العبارة المضافة إلى الدالة cout عند استدعاء الدالة Eat().
- نهاية جسم الدالة Eat().
- نهاية الجسم من فئة الثدييات.
- قم بإنشاء فئة تسمى البقرة التي ترث فئة الثدييات. البقرة هي الفئة المشتقة، في حين أن الثدييات هي الفئة الأساسية. يمثل { بداية هذه الفئة.
- استخدم مُعدِّل الوصول العام لتحديد الوظيفة التي نحن على وشك إنشائها على أنها متاحة للعامة. سيكون متاحًا من خارج هذا الفصل.
- تجاوز الدالة Eat() التي تم تعريفها في الفئة الأساسية. يمثل { بداية نص الوظيفة.
- العبارة التي سيتم طباعتها على وحدة التحكم عند استدعاء هذه الوظيفة.
- نهاية نص الدالة Eat().
- نهاية جسد فئة البقرة.
- استدعاء الدالة الرئيسية (). يمثل { بداية نص هذه الوظيفة.
- قم بإنشاء مثيل لفئة Cow وإعطائه الاسم c.
- قم باستدعاء الدالة Eat() المحددة في فئة Cow.
- يجب أن يقوم البرنامج بإرجاع قيمة عند اكتماله بنجاح.
- نهاية الوظيفة الرئيسية ().
C++ الوظيفة الافتراضية
تعد الوظيفة الافتراضية طريقة أخرى لتنفيذ تعدد الأشكال في وقت التشغيل C++. إنها وظيفة خاصة محددة في فئة أساسية ويتم إعادة تعريفها في الفئة المشتقة. للإعلان عن وظيفة افتراضية، يجب عليك استخدام الكلمة الأساسية الافتراضية. يجب أن تسبق الكلمة الأساسية إعلان الوظيفة في الفئة الأساسية.
إذا تم توريث فئة دالة افتراضية، فإن الفئة الافتراضية تعيد تعريف الوظيفة الافتراضية لتناسب احتياجاتها. على سبيل المثال:
#include <iostream> using namespace std; class ClassA { public: virtual void show() { cout << "The show() function in base class invoked..." << endl; } }; class ClassB :public ClassA { public: void show() { cout << "The show() function in derived class invoked..."; } }; int main() { ClassA* a; ClassB b; a = &b; a->show(); }
الإخراج:
فيما يلي لقطة شاشة للكود:
شرح الكود:
- قم بتضمين ملف رأس iostream في الكود لاستخدام وظائفه.
- قم بتضمين مساحة الاسم std في الكود الخاص بنا لاستخدام فئاتها دون استدعائها.
- قم بإنشاء فئة تسمى ClassA.
- استخدم معدّل الوصول العام لوضع علامة على عضو الفصل على أنه يمكن الوصول إليه بشكل عام.
- قم بإنشاء دالة افتراضية باسم show(). ستكون وظيفة عامة.
- النص الذي سيتم طباعته عند استدعاء الدالة show(). النهاية هي أ C++ الكلمة الأساسية، والتي تعني خط النهاية. يقوم بنقل مؤشر الماوس إلى السطر التالي.
- نهاية نص الدالة الافتراضية show().
- نهاية جسم الفئة ClassA.
- إنشاء فئة جديدة باسم ClassB ترث الفئة ClassA. تصبح ClassA هي الفئة الأساسية بينما تصبح ClassB هي الفئة المشتقة.
- استخدم معدّل الوصول العام لوضع علامة على عضو الفصل على أنه يمكن الوصول إليه بشكل عام.
- أعد تعريف الوظيفة الافتراضية show() المشتقة من الفئة الأساسية.
- النص الذي سيتم طباعته على وحدة التحكم عند استدعاء الدالة show() المحددة في الفئة المشتقة.
- نهاية نص الدالة show().
- نهاية نص الفئة المشتقة، ClassB.
- استدعاء الدالة الرئيسية (). يجب إضافة منطق البرنامج داخل جسمه.
- قم بإنشاء متغير مؤشر يسمى أ. يشير إلى الفئة المسماة ClassA.
- قم بإنشاء مثيل للفئة المسماة ClassB. تم إعطاء المثيل الاسم ب.
- قم بتعيين مخازن القيم في العنوان b في المتغير a.
- استدعاء الدالة show() المحددة في الفئة المشتقة. تم تنفيذ الربط المتأخر.
- نهاية نص الدالة main().
ترجمة تعدد الأشكال في الوقت مقابل. تعدد الأشكال في وقت التشغيل
فيما يلي الاختلافات الرئيسية بين الاثنين:
تعدد الأشكال وقت الترجمة | تعدد الأشكال في وقت التشغيل |
---|---|
ويسمى أيضًا تعدد الأشكال الثابت أو الارتباط المبكر | ويسمى أيضًا الارتباط المتأخر/الديناميكي أو تعدد الأشكال الديناميكي |
يتم استدعاء/استدعاء الطريقة أثناء وقت الترجمة | يتم استدعاء/استدعاء الطريقة أثناء وقت التشغيل |
تم التنفيذ من خلال التحميل الزائد للوظيفة والتحميل الزائد للمشغل | يتم تنفيذها عبر تجاوز الطريقة والوظائف الافتراضية |
مثال، طريقة التحميل الزائد. قد يكون للعديد من الأساليب أسماء متشابهة ولكن عدد أو أنواع مختلفة من الوسائط | مثال، طريقة التجاوز. قد يكون للعديد من الطرق اسم مشابه ونفس النموذج الأولي. |
تنفيذ أسرع نظرًا لأن اكتشاف الأساليب يتم أثناء وقت الترجمة | تنفيذ أبطأ نظرًا لأن مكتشف الطريقة يتم أثناء وقت التشغيل. |
Less يتم توفير المرونة لحل المشكلات نظرًا لأن كل شيء معروف أثناء وقت الترجمة. | يتم توفير قدر كبير من المرونة لحل المشكلات المعقدة حيث يتم اكتشاف الأساليب أثناء وقت التشغيل. |
الملخص
- تعدد الأشكال يعني أن يكون هناك أشكال عديدة.
- يحدث ذلك عندما يكون هناك تسلسل هرمي للفئات المرتبطة بالميراث.
- مع تعدد الأشكال، يمكن للوظيفة أن تتصرف بشكل مختلف بناءً على الكائن الذي يستدعيها/يستدعيها.
- في تعدد الأشكال في وقت الترجمة، يتم إنشاء الوظيفة التي سيتم استدعاؤها أثناء وقت الترجمة.
- في تعدد الأشكال في وقت التشغيل، يتم إنشاء الوظيفة التي سيتم استدعاؤها أثناء وقت التشغيل.
- يتم تحديد تعدد أشكال وقت التجميع من خلال التحميل الزائد للوظيفة والتحميل الزائد للمشغل.
- في التحميل الزائد للوظائف، هناك العديد من الوظائف بأسماء متشابهة ولكن وسائط مختلفة.
- يمكن أن تختلف المعلمات في العدد أو النوع.
- في التحميل الزائد للمشغل، يتم تعريف معنى جديد لـ C++ مشغلي.
- يتم تحقيق تعدد الأشكال في وقت التشغيل من خلال تجاوز الوظيفة.
- في تجاوز الوظائف، تعطي الفئة المشتقة تعريفًا جديدًا لوظيفة محددة في الفئة الأساسية.