استدعاء الطريقة الأصلية في C++: دليل شامل للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مشكلة يعرفها الجميع: "لماذا لا يعمل الكود الخاص بي؟"

\n\n

هل سبق لك أن كتبت فئة مشتقة، وأعدت تعريف طريقة فيها، ثم اكتشفت بدهشة أن الوظيفة الأساسية للفئة الأصلية قد اختفت في مكان ما؟ مألوف؟ هذه الحالة هي كلاسيكية من نوعها في عالم C++. السؤال "How to call a parent class function from derived class function" على Stack Overflow حصد آلاف المشاهدات ومئات المناقشات. وليس من المستغرب: بدون فهم هذه الآلية، ستستمر في الوقوع في نفس الأخطاء مرارًا وتكرارًا.

\n\n

اليوم سنشرح هذا الموضوع مرة واحدة وإلى الأبد. لن تتعلم فقط بناء جملة استدعاء دالة الفئة الأصلية، بل ستفهم أيضًا متى يكون ذلك ضروريًا حقًا، ومتى يكون ضارًا. هيا بنا!

\n\n

الأساسيات: الوراثة وإعادة تعريف الطرق

\n\n

قبل أن نتحدث عن استدعاء دوال الفئة الأصلية، دعنا نتذكر كيف تعمل الوراثة في C++. تخيل أن لديك فئة أساسية Animal بطريقة speak(). تقوم بإنشاء فئة Dog ترث من Animal، وتعيد تعريف speak() لكي ينبح الكلب بدلاً من إصدار صوت عام.

\n\n
#include <iostream>\nusing namespace std;\n\nclass Animal {\npublic:\n    void speak() {\n        cout << "Animal speaks!" << endl;\n    }\n};\n\nclass Dog : public Animal {\npublic:\n    void speak() {\n        cout << "Dog barks!" << endl;\n    }\n};\n\nint main() {\n    Dog myDog;\n    myDog.speak(); // الإخراج: Dog barks!\n    return 0;\n}
\n\n

كل شيء يعمل. ولكن ماذا لو أردت أن تقوم Dog::speak() أولاً بتنفيذ إجراء الفئة الأصلية، ثم تضيف إجراءها الخاص؟ على سبيل المثال: "الحيوان يصدر صوتًا، والكلب ينبح". هنا يبدأ الجزء المثير للاهتمام.

\n\n

لماذا لا يمكن استدعاء الطريقة الأصلية ببساطة؟

\n\n

عند إعادة تعريف طريقة في الفئة المشتقة، يعتبر C++ أنك قد استبدلت السلوك بالكامل. تصبح الطريقة الأصلية "غير مرئية" داخل سياق الفئة المشتقة. إذا كتبت ببساطة speak() داخل Dog::speak()، فسيستدعي المترجم نفسه - مما يؤدي إلى استدعاء متكرر لا نهائي.

\n\n

الحل: عامل تحديد نطاق الرؤية (::)

\n\n

لتوجيه المترجم بوضوح: "مرحبًا، أريد استدعاء الطريقة من الفئة الأصلية بالتحديد!"، يتم استخدام العامل ::. بناء الجملة بسيط: اسمالفئةالأصلية::اسمالطريقة().

\n\n
class Dog : public Animal {\npublic:\n    void speak() {\n        Animal::speak(); // استدعاء الطريقة الأصلية\n        cout << "Dog barks!" << endl;\n    }\n};\n\nint main() {\n    Dog myDog;\n    myDog.speak();\n    // الإخراج:\n    // Animal speaks!\n    // Dog barks!\n    return 0;\n}
\n\n

لاحظ: نحن لا ننشئ كائنًا جديدًا من الفئة الأصلية. نحن فقط نقول: "خذ تنفيذ speak() من الفئة Animal ونفذه في سياق الكائن الحالي Dog". هذه آلية قوية تسمح ببناء سلاسل من الاستدعاءات.

\n\n

متى يكون ذلك ضروريًا حقًا؟ 3 سيناريوهات واقعية

\n\n

1. توسيع الوظائف (الحالة الأكثر شيوعًا)

\n\n

تريد إضافة سلوك جديد دون فقدان السلوك القديم. المثال الكلاسيكي هو المنشئات والمدمرات. نعم، يتم استدعاؤها تلقائيًا، ولكن إذا كنت تعيد تعريف طريقة تهيئة ما، فإن استدعاء الفئة الأصلية إلزامي.

\n\n
class Vehicle {\npublic:\n    void start() {\n        cout << "Engine started. " << endl;\n    }\n};\n\nclass Car : public Vehicle {\npublic:\n    void start() {\n        Vehicle::start(); // أولاً نقوم بتشغيل المحرك\n        cout << "Car is ready to drive!" << endl;\n    }\n};
\n\n

2. العمل مع الدوال الافتراضية وتعدد الأشكال

\n\n

إذا تم تعريف الطريقة الأساسية كـ virtual، فقد يكون استدعاء النسخة الأصلية مفيدًا لتنفيذ "السلوك الأساسي الافتراضي"

المدونات

توصيات الكتب