Menu
flag Ar iconالعربيةdown icon

محددات الوصول في C++‎: public وprivate وprotected

كيف تتحكم public وprivate وprotected في من يستطيع لمس أعضاء الصنف في C++‎ - أساس التغليف، مع الـ getters والـ setters ومنفذ الهروب friend.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

من يحق له لمس بياناتك

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

هناك ثلاثة بالضبط: public وprivate وprotected. يضع كل منها وسمًا على الأعضاء التي تليه، والوسم يقرر أي كود يُسمح له بقراءتها أو الكتابة إليها. إن أحسنت ذلك فرض صنفك قواعده الخاصة؛ وإن أخطأت فقد يفسد أي خطأ في أي مكان حالة كائنك.

المحددات الثلاثة

المحدِّد كلمة مفتاحية تتبعها نقطتان رأسيتان. وكل عضو يُعلن بعده - حتى المحدِّد التالي - يندرج تحت ذلك المستوى من الوصول.

أزل التعليق عن السطر الأخير ويرفض المترجم البناء: balance خاص (private)، لذا لا يستطيع main العبث به مباشرة. هذا هو المقصود - الطريقة الوحيدة لتغيير الرصيد هي عبر deposit، ما يعني أنه يمكنك لاحقًا إضافة تحقق (منع الإيداعات السالبة، والتسجيل، والحدود) في مكان واحد والوثوق بأنه يُطبَّق دائمًا.

وإليك التفصيل الكامل:

//             يمكن الوصول إليه من...
// public      أي مكان (أي كود يملك الكائن)
// private     أعضاء الصنف نفسه فقط (+ friends)
// protected   أعضاء الصنف نفسه والأصناف المشتقة (+ friends)

class مقابل struct: الافتراضي

يمكنك كتابة ما تشاء من كتل المحددات بأي ترتيب. أما ما يحصل عليه الأعضاء قبل أن تكتب المحدِّد الأول فيعتمد على ما إذا كنت استخدمت class أم struct:

  • في class، الأعضاء private افتراضيًا.
  • في struct، الأعضاء public افتراضيًا.

هذا الافتراضي هو الفرق الوحيد على مستوى اللغة بين الكلمتين المفتاحيتين. فبإمكان struct أن يحتوي على دوال ومنشئات وأقسام private تمامًا كما class.

العُرف هو استخدام struct لحزم بسيطة من البيانات العامة واستخدام class حين تريد سلوكًا وحالة مخفية - لكن المترجم لا يفرض ذلك، إنما يختلف الافتراضي فقط.

التغليف عبر الـ getters والـ setters

النمط اليومي هو: تذهب البيانات إلى قسم private، وتمنح دالة public وصولًا محكومًا. يعيد الـ getter للقراءة فقط القيمة؛ ويتحقق الـ setter قبل الإسناد. وهنا يؤتي private ثماره.

ولأن celsius خاص (private)، فلا سبيل لتسريب قيمة غير صالحة - فكل كتابة يجب أن تمر عبر setCelsius، الذي يحرس الثابت المنطقي (invariant). لاحظ أن الـ getters موسومة بـ const: فهي تَعِد بألا تعدّل الكائن، لذا يمكنك استدعاؤها على كائنات const Temperature أيضًا.

protected والوراثة

لا تكون لـ protected أهمية إلا حين تدخل الوراثة المشهد. فهي تتصرف مثل private تجاه الكود الخارجي، لكن الصنف المشتق يستطيع الوصول إليها. استخدمها للأعضاء التي يحتاجها صنف فرعي بحق لكن لا ينبغي مع ذلك أن تكون متاحة للعموم.

من الأخطاء الشائعة لدى المبتدئين اللجوء إلى protected على كل عضو بيانات "تحسبًا لاحتياج صنف فرعي إليه". وهذا يوسّع عقد صنفك بصمت - فالآن يمكن لكل صنف فرعي أن يعتمد على ذلك الحقل، ولا تستطيع تغييره بحرية. فضّل private ولا ترفّعه إلى protected إلا حين يحتاج صنف مشتق الوصول حاجةً حقيقية.

منفذ الهروب friend

أحيانًا تحتاج دالة أو صنف خارجي واحد بحق إلى رؤية دواخلك - من الحالات الكلاسيكية معامل مثل << لا يمكنك جعله عضوًا. تمنح الكلمة المفتاحية friend تلك الجهة المسمّاة الواحدة الوصول إلى أعضائك private وprotected، ولا شيء غير ذلك.

friend استثناء مقصود ودقيق - فالصنف نفسه يسمّي بالضبط من يثق به، بحيث لا يستطيع أحد أن يمنح نفسه الوصول من الخارج. استخدمه باعتدال؛ فإن وجدت نفسك تضيف الكثير من friends، فالأرجح أن أعضاءك ما كان ينبغي أن تكون private من الأساس، أو أن تصميمك يحتاج إلى إعادة نظر.

أخطاء شائعة يجب تجنّبها

  • جعل كل عضو public. يبدو سهلًا، لكنك تخسر كل التحقق والثوابت المنطقية التي يوفرها التغليف. اجعل الافتراضي بيانات private مع دوال public.
  • نسيان افتراضي class. class Foo { int x; }; يجعل x خاصًا (private)، لذا لن يُترجم foo.x = 5. إن كنت تقصد حزمة بيانات بسيطة، فاستخدم struct أو أضف وسم public:.
  • الإفراط في استخدام protected. فهو حدّ أضعف من private ولا صلة له إلا بالوراثة. واللجوء إليه في كل مكان يربط الأصناف الفرعية بحقول قد تريد تغييرها.
  • توقّع أن private ميزة أمنية. إنها قاعدة في وقت الترجمة تمنع الوصول العَرَضي، وليست تشفيرًا. فالبايتات ما زالت في الذاكرة؛ وprivate يتعلق بالتصميم النظيف، لا بالسرّية.

التالي: البُنى (Structs)

رأيت الآن أن struct ليست في الحقيقة إلا class أعضاؤها public افتراضيًا. الصفحة التالية، البُنى، تتعمق في متى يكون هذا الافتراضي - public افتراضيًا - هو بالضبط ما تريد - تجميعات خفيفة لتجميع القيم المترابطة - وكيف تُستخدم struct في C++‎ الاصطلاحي إلى جانب الأصناف الكاملة الميزات.

الأسئلة الشائعة

ما الفرق بين public وprivate وprotected في C++‎؟

أعضاء public يمكن الوصول إليها من أي مكان. أعضاء private لا يمكن الوصول إليها إلا من داخل الصنف نفسه (ومن friend-اته). أما protected فهي مثل private لكنها تسمح أيضًا للأصناف المشتقة بالوصول إلى العضو. وبحسب العُرف تُبقي البيانات private وتكشف السلوك عبر دوال public.

هل الأعضاء خاصة افتراضيًا في C++‎؟

في class، نعم - كل شيء private إلى أن تكتب محدد وصول. أما في struct فالافتراضي هو public. هذا الافتراضي الوحيد هو الفرق الحقيقي الوحيد بين class وstruct في C++‎؛ وكلاهما يمكن أن يحتوي على دوال ومنشئات ومحددات وصول.

ماذا تفعل الكلمة المفتاحية friend في C++‎؟

تمنح friend دالة أو صنفًا محددًا واحدًا الوصول إلى أعضائك private وprotected. إنها استثناء مقصود وضيق للتغليف - فالصنف يسمّي بدقة من يثق به، بحيث لا يُمنح الوصول ضمنيًا أبدًا.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن