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

محددات الوصول بشكل متعمق

جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 35 من 104.

الآن بعد أن فهمت محددات الوصول الثلاثة، دعنا نستكشف كيف تتصرف عندما يكون لديك كائنات متعددة من نفس الـ class. من المفاهيم الخاطئة الشائعة أن private تعني "هذا الكائن فقط" - ولكنها في الواقع تعني "هذه الـ class فقط".

يمكن للكائنات من نفس الفئة الوصول إلى الأعضاء الخاصين (private members) لبعضها البعض:

class Person {
    std::string secret;
    
public:
    Person(std::string s) : secret(s) {}
    
    void readSecret(const Person& other) {
        // الوصول إلى العضو الخاص لكائن آخر - مسموح به!
        std::cout << other.secret;
    }
};

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

يمكنك أيضاً التبديل بين محددات الوصول عدة مرات داخل الفئة (class):

class Mixed {
public:
    void publicMethod1();
    
private:
    int privateData;
    
public:
    void publicMethod2();    // العودة إلى العام (public)
    
private:
    void privateHelper();    // خاص (private) مرة أخرى
};

بينما يعد هذا كود C++ صالحاً، فمن الأفضل عموماً تجميع الأعضاء حسب مستوى الوصول من أجل سهولة القراءة. توصي معظم أدلة الأنماط بإدراج الأعضاء public أولاً (بما أنهم يشكلون واجهة class)، يليهم protected، ثم private.

challenge icon

التحدي

سهل

لنقم ببناء نظام مقارنة للمحافظ يوضح كيف يمكن للكائنات من نفس الفئة (class) الوصول إلى الأعضاء الخاصة (private members) لبعضها البعض. هذه رؤية أساسية حول التحكم في الوصول في لغة C++ — حيث يتم فرضه على مستوى الفئة، وليس على مستوى الكائن.

ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:

  • Wallet.h: قم بتعريف فئة Wallet التي تخزن البيانات المالية الخاصة ولكنها تسمح بإجراء مقارنات بين محفظة وأخرى. يجب أن تحتوي الفئة الخاصة بك على:

    • أعضاء خاصة (Private members): ownerName (سلسلة نصية string) و balance (رقم عشري double)
    • مُنشئ (Constructor) يأخذ اسم المالك والرصيد الأولي
    • دالة جلب عامة (public getter) باسم getOwnerName() تعيد اسم المالك
    • دالة hasMoreThan(const Wallet& other) تقارن رصيد هذه المحفظة بالرصيد الخاص لمحفظة أخرى، وتعيد true إذا كانت هذه المحفظة تمتلك مالاً أكثر
    • دالة combinedBalance(const Wallet& other) تعيد مجموع الأرصدة الخاصة لكلتا المحفظتين
    • دالة transferTo(Wallet& other, double amount) تقوم بنقل المال من هذه المحفظة إلى محفظة أخرى — يجب أن تقوم بتعديل الأرصدة الخاصة لكلتا المحفظتين مباشرة (فقط إذا كان المبلغ موجباً ولا يتجاوز رصيد هذه المحفظة)
  • main.cpp: استعرض الوصول إلى الأعضاء الخاصة عبر الكائنات. اقرأ اسمين للمالكين ورصيدين من المدخلات (أربعة أسطر: name1، balance1، name2، balance2)، ثم:

    • أنشئ كائنين من نوع Wallet باستخدام القيم المدخلة
    • اطبع "Combined wealth: "Combined wealth: $"lt;amount>" باستخدام combinedBalance()
    • قارن بين المحفظتين واطبع "<name> has more money" للمالك الذي لديه الرصيد الأعلى (استخدم hasMoreThan() لتحديد ذلك)
    • قم بتحويل 100.0 من المحفظة الأولى إلى الثانية باستخدام transferTo()
    • اطبع "After transfer:"
    • اطبع "Combined wealth: "Combined wealth: $"lt;amount>" مرة أخرى (يجب أن يظل دون تغيير)
    • قارن مرة أخرى واطبع "<name> has more money" (قد تكون النتيجة قد تغيرت)

المفهوم الأساسي هنا هو أن الدوال مثل hasMoreThan() و transferTo() يمكنها الوصول مباشرة إلى عضو balance الخاص بكائن Wallet آخر — دون الحاجة إلى دوال جلب (getters). يعمل هذا لأن التحكم في الوصول في C++ يعتمد على الفئة: أي كود داخل فئة Wallet يمكنه الوصول إلى الأعضاء الخاصة لـ أي كائن من نوع Wallet.

قم بتنسيق جميع القيم المالية بمنزلتين عشريتين باستخدام std::fixed و std::setprecision(2) من مكتبة <iomanip>. قم بتحويل مدخلات الرصيد باستخدام std::stod().

ورقة مرجعية

يتم فرض التحكم في الوصول في لغة C++ على مستوى الفئة (class level)، وليس على مستوى الكائن (object level). وهذا يعني أن الكائنات من نفس الفئة يمكنها الوصول إلى الأعضاء الخاصين (private members) لبعضها البعض:

class Person {
    std::string secret;
    
public:
    Person(std::string s) : secret(s) {}
    
    void readSecret(const Person& other) {
        // الوصول إلى عضو خاص لكائن آخر - مسموح به!
        std::cout << other.secret;
    }
};

يمكن للدالة readSecret الوصول إلى العضو الخاص secret لأي كائن من نوع Person لأنها جزء من فئة Person. يتيح هذا التصميم عمليات مثل منشئات النسخ (copy constructors) وعوامل المقارنة (comparison operators).

يمكنك التبديل بين محددات الوصول (access specifiers) عدة مرات داخل الفئة:

class Mixed {
public:
    void publicMethod1();
    
private:
    int privateData;
    
public:
    void publicMethod2();    // العودة إلى العام (public)
    
private:
    void privateHelper();    // خاص (private) مرة أخرى
};

ومع ذلك، فمن الأفضل تجميع الأعضاء حسب مستوى الوصول من أجل سهولة القراءة. توصي معظم أدلة التنسيق (style guides) بإدراج الأعضاء العامين (public) أولاً، يليهم المحميون (protected)، ثم الخاصون (private).

جرّب بنفسك

#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: طباعة الثروة الإجمالية باستخدام combinedBalance()
    // التنسيق: "Combined wealth: $<amount>"
    
    // TODO: مقارنة المحافظ باستخدام hasMoreThan() وطباعة من يملك مالاً أكثر
    // التنسيق: "<name> has more money"
    
    // TODO: تحويل 100.0 من المحفظة الأولى إلى الثانية باستخدام transferTo()
    
    // TODO: طباعة "After transfer:"
    
    // TODO: طباعة الثروة الإجمالية مرة أخرى
    
    // TODO: المقارنة مرة أخرى وطباعة من يملك مالاً أكثر
    
    return 0;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming