مستويات الوصول في الوراثة
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 49 من 104.
يؤثر نوع الوراثة الذي تختاره على كيفية الوصول إلى أعضاء الفئة الأساسية (base class) في الفئة المشتقة (derived class). توفر لغة C++ ثلاثة خيارات: الوراثة من النوع public، و protected، و private.
إليك كيف يغير كل نوع من أنواع الوراثة إمكانية الوصول إلى الأعضاء:
| عضو القاعدة (Base Member) | الوراثة العامة (public inheritance) | الوراثة المحمية (protected inheritance) | الوراثة الخاصة (private inheritance) |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | غير قابل للوصول (inaccessible) | غير قابل للوصول (inaccessible) | غير قابل للوصول (inaccessible) |
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pub عام (public)، و prot محمي (protected)، و priv لا يمكن الوصول إليه
};
class ProtectedDerived : protected Base {
// pub محمي (protected)، و prot محمي (protected)، و priv لا يمكن الوصول إليه
};
class PrivateDerived : private Base {
// pub خاص (private)، و prot خاص (private)، و priv لا يمكن الوصول إليه
};الوراثة العامة تجسد علاقات "is-a" وهي الأكثر شيوعاً على الإطلاق. فالـ Dog هو Animal، لذا يمكن للكود الخارجي التعامل معه على هذا الأساس.
تمثل الوراثة المحمية والخاصة علاقات "مُنفذ بدلالة" (implemented-in-terms-of). فهي تخفي واجهة الفئة الأساسية عن الكود الخارجي، وهو أمر مفيد عندما تريد إعادة استخدام التنفيذ دون الكشف عن علاقة الوراثة. ومع الوراثة الخاصة، تفقد حتى الفئات المشتقة اللاحقة إمكانية الوصول إلى أعضاء الفئة الأساسية الأصلية.
التحدي
سهلدعنا نستكشف كيف تؤثر أنواع الوراثة المختلفة على إمكانية الوصول إلى الأعضاء من خلال بناء تسلسل هرمي بسيط للأجهزة. ستقوم بإنشاء فئة أساسية Device وثلاث فئات مشتقة، كل منها يستخدم نوع وراثة مختلف، لترى بنفسك كيف تقوم الوراثة العامة (public)، والمحمية (protected)، والخاصة (private) بتحويل الوصول إلى الأعضاء.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
Device.h: قم بتعريف فئة أساسيةDeviceمع ثلاثة أعضاء بمستويات وصول مختلفة:- عضو عام (public) من نوع
std::string model - عضو محمي (protected) من نوع
int serialNumber - عضو خاص (private) من نوع
int internalCode(يتم ضبطه على 999 افتراضياً) - دالة عامة (public method) باسم
showModel()تقوم بطباعة:Model: <model>
ثم قم بإنشاء ثلاث فئات مشتقة ترث كل منها من
Deviceباستخدام أنواع وراثة مختلفة:PublicDeviceباستخدام الوراثة العامة (public inheritance) — أضف دالةdisplaySerial()تقوم بطباعة:Serial: <serialNumber>ProtectedDeviceباستخدام الوراثة المحمية (protected inheritance) — أضف دالةdisplaySerial()تقوم بطباعة:Serial: <serialNumber>PrivateDeviceباستخدام الوراثة الخاصة (private inheritance) — أضف دالةdisplaySerial()تقوم بطباعة:Serial: <serialNumber>
يجب أن تحتوي كل فئة مشتقة على منشئ (constructor) يأخذ اسم الطراز (model name) والرقم التسلسلي (serial number)، ويمرر الطراز إلى الفئة الأساسية ويقوم بضبط الرقم التسلسلي.
- عضو عام (public) من نوع
main.cpp: اقرأ مدخلين (كل منهما في سطر منفصل):- اسم الطراز (string)
- الرقم التسلسلي (integer)
قم بإنشاء كائن واحد من كل نوع مشتق بنفس قيم المدخلات. ثم وضح اختلافات الوصول:
بالنسبة لـ
PublicDevice:- استدعِ
showModel()مباشرة على الكائن (يعمل لأن الوراثة العامة تبقيها عامة) - الوصول إلى
modelمباشرة واطبع:Direct model access: <model> - استدعِ
displaySerial()
بالنسبة لـ
ProtectedDeviceوPrivateDevice:- استدعِ فقط
displaySerial()(حيث أنshowModel()وmodelالموروثين لم يعد من الممكن الوصول إليهما علناً من الخارج)
تنسيق المخرجات:
--- PublicDevice --- Model: <model> Direct model access: <model> Serial: <serial> --- ProtectedDevice --- Serial: <serial> --- PrivateDevice --- Serial: <serial>
على سبيل المثال، مع المدخلات Laptop و 12345:
--- PublicDevice ---
Model: Laptop
Direct model access: Laptop
Serial: 12345
--- ProtectedDevice ---
Serial: 12345
--- PrivateDevice ---
Serial: 12345لاحظ كيف يسمح PublicDevice فقط بالوصول الخارجي إلى الأعضاء العامة الموروثة. تخفي أنواع الوراثة الأخرى واجهة الفئة الأساسية عن الكود الخارجي، ولكن لا يزال بإمكان الفئات المشتقة الوصول إلى الأعضاء المحمية داخلياً من خلال دوالها الخاصة.
ورقة مرجعية
تقدم لغة C++ ثلاثة أنواع من الوراثة التي تتحكم في كيفية الوصول إلى أعضاء الفئة الأساسية (base class) في الفئات المشتقة (derived classes):
| عضو الفئة الأساسية | الوراثة العامة (public) | الوراثة المحمية (protected) | الوراثة الخاصة (private) |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | لا يمكن الوصول إليه | لا يمكن الوصول إليه | لا يمكن الوصول إليه |
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pub هو public، و prot هو protected، و priv لا يمكن الوصول إليه
};
class ProtectedDerived : protected Base {
// pub هو protected، و prot هو protected، و priv لا يمكن الوصول إليه
};
class PrivateDerived : private Base {
// pub هو private، و prot هو private، و priv لا يمكن الوصول إليه
};تمثل الوراثة العامة (Public inheritance) علاقة "هو-نوع-من" (is-a) وهي الأكثر شيوعاً. يمكن للكود الخارجي الوصول إلى الأعضاء العامة للفئة الأساسية من خلال الفئة المشتقة.
تمثل الوراثة المحمية والخاصة (Protected and private inheritance) علاقة "مُنفذ-بدلالة" (implemented-in-terms-of). فهي تخفي واجهة الفئة الأساسية عن الكود الخارجي. ومع الوراثة الخاصة، تفقد حتى الفئات المشتقة اللاحقة القدرة على الوصول إلى أعضاء الفئة الأساسية الأصلية.
جرّب بنفسك
#include <iostream>
#include <string>
#include "Device.h"
using namespace std;
int main() {
// قراءة المدخلات
string modelName;
int serialNum;
getline(cin, modelName);
cin >> serialNum;
// TODO: إنشاء كائنات من كل نوع مشتق باستخدام قيم المدخلات
// PublicDevice pubDev(modelName, serialNum);
// ProtectedDevice protDev(modelName, serialNum);
// PrivateDevice privDev(modelName, serialNum);
// TODO: توضيح الوصول إلى PublicDevice
cout << "--- PublicDevice ---" << endl;
// - استدعاء showModel() مباشرة على الكائن
// - الوصول إلى model مباشرة وطباعة: Direct model access: <model>
// - استدعاء displaySerial()
// TODO: توضيح الوصول إلى ProtectedDevice
cout << "--- ProtectedDevice ---" << endl;
// - استدعاء displaySerial() فقط (لا يمكن الوصول إلى showModel و model بشكل عام)
// TODO: توضيح الوصول إلى PrivateDevice
cout << "--- PrivateDevice ---" << endl;
// - استدعاء displaySerial() فقط (لا يمكن الوصول إلى showModel و model بشكل عام)
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةبناء وتجميع (Compilation) لغة C++ملفات الـ Header وملفات الـ Sourceالـ Namespaces والـ Scopeمقدمة في الـ OOP في C++الـ Classes مقابل الـ Objectsالمؤشر 'this'الـ Methods (الدوال الأعضاء)الـ Attributes (بيانات الأعضاء)أساسيات الـ Ctors والـ Dtorsمراجعة - آلة حاسبة بسيطة4خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticدوال الـ Getters والـ Settersدوال الأعضاء الثابتة (Const)الكلمة المفتاحية Mutableالدوال والمتغيرات الساكنة (Static)الدوال والفئات الصديقة (Friend)مراجعة - مدير الحساب البنكي7الوراثة
الوراثة الأساسيةمستويات الوصول في الوراثةترتيب استدعاء الـ Ctor والـ Dtorإعادة تعريف الدوال (Method Overriding)الدوال الافتراضية والـ VTableالوراثة المتعددةالوراثة الافتراضيةمراجعة - هيكلية الموظفين10نظرة عامة على STL
نظرة عامة وفلسفة STLحاويات STLالـ Iteratorsخوارزميات STLالـ Functors وتعبيرات Lambdaمراجعة - تكرار الكلمات13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factory و Abstract Factoryنمط Builderنمط Observerنمط Strategy2إدارة الذاكرة
ذاكرة Stack مقابل Heapالمؤشرات والمراجعالذاكرة الديناميكية (new/delete)المؤشرات الذكية في C++RAII في C++مراجعة - مدير المصفوفات الديناميكية5التغليف (Encapsulation)
محددات الوصول في C++محددات الوصول بشكل متعمقإخفاء المعلوماتStruct مقابل Classالأصناف المتداخلة والداخليةمراجعة - نظام سجلات الطلاب8تعدد الأشكال (Polymorphism)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالتحميل الزائد للدوال (Function Overloading)مراجعة الدوال الافتراضية (Virtual Functions)الدوال الافتراضية البحتة (Pure Virtual Functions)الأصناف المجردة (Abstract Classes)تصميم الواجهات (Interface) في C++التحويل الديناميكي (Dynamic Casting) و RTTIملخص - حاسبة الأشكال11مفاهيم OOP المتقدمة
التركيب مقابل الوراثةالـ Mixins عبر CRTPنمط Pimplمحو النوع (Type Erasure)أصناف Enum والأنواع القويةمعالجة الاستثناءات في OOPتسلسلات الاستثناءات المخصصة14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط قالب الطريقةنمط الحالةنمط التركيبRAII كنمط3المنشئات والموادم
المنشئ الافتراضيالمنشئ ذو المعاملاتمنشئ النسخمنشئ النقلقوائم تهيئة المنشئالمنشئات المفوضةتعمق في الموادمقاعدة الثلاثة / الخمسة / الصفرمراجعة - فئة String6تحميل العوامل (Operator Overloading)
مقدمة في تحميل العواملتحميل العوامل الحسابيةتحميل عوامل المقارنةعوامل Streamتحميل عامل التعيينتحميل العوامل [] و ()عوامل تحويل النوعمراجعة - Matrix Class9القوالب
قوالب الدوالقوالب الأصنافتخصيص القوالبالقوالب المتغيرةأساسيات SFINAE و Type Traitsمراجعة - الحاويات العامة