الوراثة في السي بلاس بلاس | C++ Inheritance


 

مفهوم الوراثة  في السي بلاس بلاس   

في البداية, كلمة وراثة تعني تضمين محتوى كلاس في كلاس آخر.
في C++, الكلاس يمكنه أن يرث من كلاس آخر حتى يحصل على الدوال و المتغيرات الموجودة فيه.

إذاً فكرة الوراثة بسيطة لكن فائدتها قوية جداً. فمثلاً إذا كنت تريد إنشاء كلاس جديد و لاحظت أنه يوجد كلاس جاهز يحتوي على كودات قد تفيدك يمكنك استغلالها بدل كتابتها من الصفر, أي يمكنك جعل الكلاس الذي قمت بتعريفه يرث هذا الكلاس, و بعدها يمكنك إستخدام جميع المتغيرات و الدوال التي ورثها الكلاس الجديد من الكلاس الجاهز.

 مصطلحات تقنية
  • الوراثة تسمى Inheritance في اللغة الإنجليزية.

  • الكلاس الذي يرث من كلاس آخر يقال له الكلاس الإبن, و يسمى Subclass و يقال له أيضاً Derived ClassExtended Class أو Child Class ).

  • الكلاس الذي يورّث محتوياته لكلاس آخر يسمى الكلاس الأب, و يسمى Superclass و يقال له أيضاً Base Class أو Parent Class ).

أشكال الوراثة Inheritance في السي بلاس بلاس   

في C++ يوجد 4 أشكال للوراثة كما في الجدول التالي.

أشكال الوراثة في السي بلاس بلاس


إذاَ أشكال الوراثة في C++ هي كالتالي:

  • وراثة فردية: تعني كلاس يرث من كلاس واحد فقط.

  • وراثة متتالية: تعني كلاس يرث من كلاس واحد و هذا الكلاس كان في الأصل يرث من كلاس آخر.

  • وراثة هرمية: تعني أن الكلاس موروث من قبل أكثر من كلاس.

  • وراثة متعددة: تعني أن الكلاس يرث من أكثر من كلاس.

كيفية جعل الكلاس يرث من كلاس آخر في C++

لجعل الكلاس يرث من كلاس آخر نتبع الأسلوب التالي.

class derived-class : access-specifier base-class
{

}

  • derived-class: &nbsp يقصد بها إسم الكلاس الإبن.

  • base-class: &nbsp يقصد بها إسم الكلاس الأب.

  • access-specifier: &nbsp يقصد بها تحديد كيف يمكن الوصول للأشياء التي تم تضمينها في الكلاس الأب في الكلاس الإبن.


إذاَ بالمبدأ لجعل الكلاس يرث من كلاس آخر نضع بعد إسم الكلاس نقطتين فوق بعض, ثم إسم الكلاس الذي نريده أن يرث منه.
مكان الكلمة access-specifier يمكنك وضع public أو private أو protected أو عدم وضع أي كلمة منهم و عندها سيتم إعتبار أنك وضعت الكلمة private.

ماهي كلمات الوصول في C++

عندما تجعل الكلاس يرث من كلاس آخر يمكنك استخدام إحدى كلمات الوصول (Access Specifier ) لتحدد كيف يمكن الوصول للأشياء التي ورثها الكلاس الإبن من الكلاس الأب.

 

ماهي الكلمة public في C++

تستخدم لتحديد أن الأشياء التي سيرثها الكلاس الإبن من الكلاس الأب سيكون الوصول لها ممكناً من أي مكان.

مثال: class B : public A
هنا أي شيء يرثه الكلاس B من الكلاس A سيتم إعتبار كأنه تم تعريفه public في الكلاس B.


 

ماهي الكلمة private في C++

تستخدم لتحديد أن الأشياء التي سيرثها الكلاس الإبن من الكلاس الأب يمكن الوصول لها من نفس الكلاس فقط.

 

مثال: class B : private A
هنا أي شيء يرثه الكلاس B من الكلاس A سيتم إعتبار كأنه تم تعريفه private في الكلاس B.



شرح الكلمة protected في C++

تستخدم لتحديد أن الأشياء التي سيرثها الكلاس الإبن من الكلاس الأب يمكن الوصول لها من نفس اكلاس و من أي كلاس آخر يرثها.

مثال: class B : protected A
هنا أي شيء يرثه الكلاس B من الكلاس A سيتم إعتبار كأنه تم تعريفه protected في الكلاس B.

 

أمثلة تطبيقية على الوراثة Inheritance في C++


مثال عن الوراثة الفردية في C++

في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على متغير إسمه x و دالة إسمها printMessage().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على متغير إسمه y و يرث من الكلاس A.

شكل الوراثة Inheritance سيكون كالتالي.

مثال عن الوراثة الفردية في C++

المثال الأول عن الوراثة Inheritance الفردية في C++

main.cpp
                    #include <iostream>

                    using namespace std;

                    // printMessage و دالة إسمها x يحتوي على متغير إسمه A هنا قمنا بتعريف كلاس إسمه
                    class A {

                    public: 
                    int x = 10;

                    void printMessage()
                    {
                    cout << "Hello from class A \n";
                    }

                    };

                    // y و يحتوي على متغير إسمه A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
                    class B: public A {

                    public:
                    int y = 20;

                    };

                    // main() هنا قمنا بتعريف الدالة
                    int main()
                    {
                    // b إسمه B هنا قمنا بإنشاء كائن من الكلاس
                    B b;

                    // B و الذي تم تعريفه في الكلاس b الموجود في الكائن y هنا قمنا بطباعة قيمة المتغير
                    cout << "y = " << b.y << "\n";

                    // A من الكلاس B  و الذي ورثه الكلاس b الموجود في الكائن x هنا قمنا بطباعة قيمة المتغير
                    cout << "x = " << b.x << "\n";

                    // A من الكلاس B و التي ورثها الكلاس b الموجودة في الكائن printMessage() هنا قمنا باستدعاء الدالة
                    b.printMessage();

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    y = 20
                    x = 10
                    Hello from class A
                  


مثال عن الوراثة Inheritance المتتالية في C++

في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها printA().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها printB() و يرث من الكلاس A.
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها printC() و يرث من الكلاس B.

شكل الوراثة سيكون كالتالي.

مثال عن الوراثة المتتالية في C++

المثال الثاني عن الوراثة Inheritance المتتالية في C++

main.cpp
                    #include <iostream>

                    using namespace std;

                    // printA يحتوي على دالة إسمها A هنا قمنا بتعريف كلاس إسمه
                    class A {

                    public: 
                    void printA()
                    {
                    cout << "Hello from class A \n";
                    }

                    };

                    // printB و يحتوي على دالة إسمها A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
                    class B: public A {

                    public:
                    void printB()
                    {
                    cout << "Hello from class B \n";
                    }

                    };

                    // printC و يحتوي على دالة إسمها B يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه
                    class C: public B {

                    public:
                    void printC()
                    {
                    cout << "Hello from class C \n";
                    }

                    };

                    // main() هنا قمنا بتعريف الدالة
                    int main()
                    {
                    // c إسمه C هنا قمنا بإنشاء كائن من الكلاس
                    C c;

                    // c هنا قمنا باستدعاء جميع الدوال الموجودة في الكائن
                    c.printA();
                    c.printB();
                    c.printC();

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Hello from class A
                    Hello from class B
                    Hello from class C
                  


مثال عن الوراثة Inheritance المتعددة في C++

في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها printA().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها printB().
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها printC() و يرث من الكلاس A و الكلاس B.

شكل الوراثة سيكون كالتالي.

مثال عن الوراثة المتعددة في C++

المثال الثالث عن الوراثة المتعددة في C++

main.cpp
                    #include <iostream>

                    using namespace std;

                    // printA يحتوي على دالة إسمها A هنا قمنا بتعريف كلاس إسمه
                    class A {

                    public: 
                    void printA()
                    {
                    cout << "Hello from class A \n";
                    }

                    };

                    // printB يحتوي على دالة إسمها B هنا قمنا بتعريف كلاس إسمه
                    class B {

                    public:
                    void printB()
                    {
                    cout << "Hello from class B \n";
                    }

                    };

                    // printC و يحتوي على دالة إسمها B و يرث من الكلاس A يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه
                    class C: public A, public B {

                    public:
                    void printC()
                    {
                    cout << "Hello from class C \n";
                    }

                    };

                    // main() هنا قمنا بتعريف الدالة
                    int main()
                    {
                    // c إسمه C هنا قمنا بإنشاء كائن من الكلاس
                    C c;

                    // c هنا قمنا باستدعاء جميع الدوال الموجودة في الكائن
                    c.printA();
                    c.printB();
                    c.printC();

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Hello from class A
                    Hello from class B
                    Hello from class C
                  


مثال عن الوراثة Inheritance الهرمية في C++

في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها printA().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها printB() و يرث من الكلاس A.
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها printC() و يرث من الكلاس A أيضاً.

شكل الوراثة سيكون كالتالي.

مثال عن الوراثة الهرمية في C++

المثال الرابع عن الوراثة الهرمية في C++

main.cpp
                    #include <iostream>

                    using namespace std;

                    // printA يحتوي على دالة إسمها A هنا قمنا بتعريف كلاس إسمه
                    class A {

                    public: 
                    void printA()
                    {
                    cout << "Hello from class A \n";
                    }

                    };

                    // printB و يحتوي على دالة إسمها A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
                    class B : public A {

                    public:
                    void printB()
                    {
                    cout << "Hello from class B \n";
                    }

                    };

                    // printC و يحتوي على دالة إسمها A يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه
                    class C: public A {

                    public:
                    void printC()
                    {
                    cout << "Hello from class C \n";
                    }

                    };

                    // main() هنا قمنا بتعريف الدالة
                    int main()
                    {
                    // و استدعاء جميع الدوال الموجودة فيه b إسمه B هنا قمنا بإنشاء كائن من الكلاس
                    B b;
                    b.printA();
                    b.printB();

                    // و استدعاء جميع الدوال الموجودة فيه c إسمه C هنا قمنا بإنشاء كائن من الكلاس
                    C c;
                    c.printA();
                    c.printC();

                    return 0;
                    }
                  

سنحصل على النتيجة التالية عند التشغيل.

                    Hello from class A
                    Hello from class B
                    Hello from class A
                    Hello from class C
                  

مشكلة الوراثة Inheritance المتعددة في C++

عندما يقوم الكلاس بوارثة أكثر من كلاس في وقت واحد فيوجد إحتمال كبير أن يرث خصائص و دوال عندها نفس الإسم مما يؤدي لحدوث تضارب في الأسماء فتجد المترجم لا يظهر لك خطأ عند كتابة الكود, بل يظهر لك خطأ عندما تقوم بتشغيل الكود.


في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها printMessage(), و كلاس إسمه B يحتوي على دالة إسمها printMessage() أيضاً.
بعدها قمنا بإنشاء كلاس إسمه C يرث من الكلاس A و الكلاس B.

إذاً هنا الكلاس C يرث دالة إسمها printMessage() من الكلاس A و دالة إسمها printMessage() من الكلاس B, و شكل الوراثة هو كالتالي.

مشكلة الوراثة المتعددة في C++

بالمنطق, سيظهر لنا مشكلة عند محاولة إستدعاء الدالة printMessage() من الكلاس C لأن المترجم لن يعرف ما إذا كنا نريد استدعاء الدالة التي ورثها من الكلاس A أو التي ورثها من الكلاس B.

مثال على مشكلة الوراثة المتعددة في C++

main.cpp
                    #include <iostream>

                    using namespace std;

                    // printMessage يحتوي على دالة إسمها A هنا قمنا بتعريف كلاس إسمه
                    class A {

                    public: 
                    void printMessage()
                    {
                    cout << "Hello from class A \n";
                    }

                    };

                    // printMessage يحتوي على دالة إسمها B هنا قمنا بتعريف كلاس إسمه
                    class B {

                    public:
                    void printMessage()
                    {
                    cout << "Hello from class A \n";
                    }

                    };

                    // B و الكلاس A يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه
                    class C : public A, public B {

                    };

                    // main() هنا قمنا بتعريف الدالة
                    int main()
                    {
                    // c إسمه C هنا قمنا بإنشاء كائن من الكلاس
                    C c;

                    // الذي يملك دالتين بهذا الإسم c من الكائن printMessage() هنا قمنا باستدعاء الدالة
                    c.printMessage();

                    return 0;
                    }
                  

سيظهر لنا الخطأ التالي عند التشغيل.

error: request for member 'printMessage' is ambiguous|

في النهاية عليك الحذر جيداً إذا كنت تنوي إنشاء كلاس يرث من أكثر من كلاس في نفس الوقت حتى لا تقع في هذه المشكلة.