محددات الوصول بشكل متعمق
جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 35 من 104.
الآن بعد أن فهمت محددات access الثلاثة، لِنستكشف كيفية تصرّفها عندما يكون لديك عدة كائنات من class نفسها. من المفاهيم الخاطئة الشائعة أن private تعني «هذا الكائن فقط» — لكنها تعني في الواقع «هذه class فقط».
يمكن للكائنات من الفئة نفسها الوصول إلى الأعضاء الخاصة لبعضها البعض:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// الوصول إلى عضو خاص لكائن آخر - مسموح!
std::cout << other.secret;
}
};يعمل هذا لأن التحكم في access يتم فرضه على مستوى الفئة، وليس على مستوى الكائن. تُعدّ الطريقة readSecret جزءًا من الفئة Person، لذا يمكنها الوصول إلى الأعضاء الخاصين لأي كائن من نوع Person. يتيح هذا التصميم لعمليات مثل مُنشئات النسخ وعوامل المقارنة العمل مع الحالة الداخلية للكائنات الأخرى.
يمكنك أيضًا التبديل بين محددات الوصول عدة مرات داخل الفئة:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // العودة إلى public
private:
void privateHelper(); // private مرة أخرى
};مع أن هذا صالح في C++، فمن الأفضل عمومًا تجميع الأعضاء حسب مستوى access لتحسين سهولة القراءة. توصي معظم أدلة الأسلوب بإدراج الأعضاء public أولًا (لأنهم يشكّلون واجهة class)، يليهم protected، ثم private.
التحدي
سهللنبنِ نظامًا لمقارنة المحافظ يوضّح كيف يمكن للكائنات من الفئة نفسها الوصول إلى الأعضاء الخاصة لبعضها بعضًا. هذه فكرة أساسية حول التحكم في الوصول في C++: إذ يُفرَض على مستوى الفئة، وليس على مستوى الكائن.
ستنشئ ملفين لتنظيم التعليمات البرمجية:
Wallet.h: عرّف فئةWalletتخزّن بيانات مالية خاصة، لكنها تسمح بإجراء المقارنات بين المحافظ. يجب أن تحتوي فئتك على:- أعضاء خاصة:
ownerName(سلسلة نصية) وbalance(عدد عشري مزدوج) - منشئ يستقبل اسم المالك والرصيد الأولي
- دالة وصول عامة
getOwnerName()تُعيد اسم المالك - دالة
hasMoreThan(const Wallet& other)تقارن رصيد هذه المحفظة بالرصيد الخاص لمحفظة أخرى، وتُعيدtrueإذا كانت هذه المحفظة تحتوي على أموال أكثر - دالة
combinedBalance(const Wallet& other)تُعيد مجموع الرصيدين الخاصين للمحفظتين - دالة
transferTo(Wallet& other, double amount)تنقل الأموال من هذه المحفظة إلى أخرى. ويجب أن تعدّل مباشرةً الرصيدين الخاصين للمحفظتين (فقط إذا كان amount موجبًا ولا يتجاوز رصيد هذه المحفظة)
- أعضاء خاصة:
main.cpp: وضّح الوصول إلى الأعضاء الخاصة عبر الكائنات. اقرأ اسمي مالكين ورصيدين من الإدخال (أربعة أسطر: name1 وbalance1 وname2 وbalance2)، ثم:- أنشئ كائنين من
Walletباستخدام قيم الإدخال - اطبع
"Combined wealth: $<amount>"باستخدامcombinedBalance() - قارن بين المحفظتين واطبع
"<name> has more money"للمالك الذي لديه الرصيد الأعلى (استخدمhasMoreThan()لتحديد ذلك) - انقل
100.0من المحفظة الأولى إلى الثانية باستخدامtransferTo() - اطبع
"After transfer:" - اطبع
"Combined wealth: $<amount>"مرة أخرى (يجب ألا يتغير) - قارن مرة أخرى واطبع
"<name> has more money"(قد تكون النتيجة قد تغيّرت)
- أنشئ كائنين من
المفهوم الأساسي هنا هو أن دوالًا مثل hasMoreThan() وtransferTo() يمكنها الوصول مباشرةً إلى عضو balance الخاص بكائن Wallet آخر: فلا حاجة إلى دوال وصول. ويعمل ذلك لأن التحكم في الوصول في C++ قائم على مستوى الفئة: إذ يمكن لأي تعليمات برمجية داخل فئة Wallet الوصول إلى الأعضاء الخاصة في أي كائن من Wallet.
نسّق جميع القيم المالية باستخدام منزلتين عشريتين عبر std::fixed وstd::setprecision(2) من <iomanip>. حوّل مدخلات الرصيد باستخدام std::stod().
جرّب بنفسك
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// اقرأ المدخلات: name1, balance1, name2, balance2
string name1, name2;
string balanceStr1, balanceStr2;
getline(cin, name1);
getline(cin, balanceStr1);
getline(cin, name2);
getline(cin, balanceStr2);
double balance1 = stod(balanceStr1);
double balance2 = stod(balanceStr2);
// اضبط تنسيق الإخراج للقيم النقدية
cout << fixed << setprecision(2);
// TODO: أنشئ كائنين من Wallet بالقيم المدخلة
// TODO: Print combined wealth using combinedBalance()
// Format: "Combined wealth: $<amount>"
// TODO: Compare wallets using hasMoreThan() and print who has more money
// Format: "<name> has more money"
// TODO: انقل 100.0 من المحفظة الأولى إلى الثانية باستخدام transferTo()
// TODO: Print "After transfer:"
// TODO: Print combined wealth again
// TODO: Compare again and print who has more money
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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مراجعة - الحاويات العامةتدرّب بنفسك: مترجم C++ عبر الإنترنت