محددات الوصول في C++
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 34 من 104.
توفر لغة C++ ثلاثة محددات وصول تتحكم في من يمكنه الوصول إلى أعضاء الفئة: public، و protected، و private. تشكل هذه المحددات أساس مفهوم التغليف (encapsulation).
class Account {
public:
std::string ownerName; // يمكن الوصول إليه من أي مكان
protected:
double interestRate; // يمكن الوصول إليه في هذا الصنف والأصناف المشتقة
private:
double balance; // يمكن الوصول إليه فقط داخل هذا الصنف
};يمكن الوصول إلى الأعضاء Public من أي مكان - داخل الفئة، ومن الفئات المشتقة، ومن الكود الخارجي. الأعضاء Private هي الأكثر تقييداً، حيث لا يمكن الوصول إليها إلا من داخل الفئة نفسها. أما Protected فهي تقع في المنتصف - فهي تعمل كأعضاء خاصة بالنسبة للعالم الخارجي، ولكن يمكن للفئات المشتقة الوصول إلى هذه الأعضاء.
class Account {
private:
double balance;
public:
Account() : balance(0) {}
void deposit(double amount) {
balance += amount; // صحيح - داخل الكلاس
}
};
int main() {
Account acc;
acc.deposit(100); // صحيح - deposit عام (public)
// acc.balance = 500; // خطأ - balance خاص (private)
}بشكل افتراضي، تكون جميع الأعضاء في class هي private إذا لم يتم إعطاء specifier. هذا يشجعك على تحديد ما يجب كشفه بشكل صريح. الممارسة العامة هي جعل أعضاء البيانات private وتوفير methods تكون public للتفاعل معها بأمان.
التحدي
سهللنقم ببناء نظام بسيط لإدارة الموظفين يوضح كيف تتحكم محددات الوصول (access specifiers) في من يمكنه الوصول إلى ماذا. ستقوم بإنشاء فئة (class) حيث تكون بعض البيانات خاصة تماماً، وبعضها متاح للفئات الفرعية المحتملة، وبعضها مفتوح للجميع.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
Employee.h: قم بتعريف فئةEmployeeتستخدم جميع محددات الوصول الثلاثة لحماية البيانات بشكل مناسب:- Public: اسم الموظف
name(string) — يمكن الوصول إليه من أي مكان - Protected: قسم الموظف
department(string) — يمكن الوصول إليه داخل الفئة ومن قبل الفئات المشتقة، ولكن ليس من الخارج - Private: راتب الموظف
salary(double) — لا يمكن الوصول إليه إلا داخل الفئة نفسها - منشئ (constructor) يأخذ الاسم والقسم والراتب لتهيئة جميع الأعضاء الثلاثة
- دالة عامة
getSalary()تعيد قيمة الراتب الخاصة - دالة عامة
getDepartment()تعيد قيمة القسم المحمية - دالة عامة
giveRaise(double amount)تزيد الراتب بمقدار معين (فقط إذا كان المبلغ موجباً) - دالة عامة
displayInfo()تطبع معلومات الموظف بالتنسيق التالي:"Name: <name>, Department: <department>, Salary: "Name: <name>, Department: <department>, Salary: $<salary>"lt;salary>"
- Public: اسم الموظف
main.cpp: وضح كيف تعمل محددات الوصول من خلال التفاعل مع كائن من نوع Employee. اقرأ اسم الموظف وقسمه وراتبه من المدخلات (ثلاثة أسطر منفصلة)، ثم:- قم بإنشاء
Employeeباستخدام القيم المدخلة - اطبع اسم الموظف مباشرة من خلال الوصول إلى العضو العام:
"Direct access - Name: <name>" - اطبع القسم باستخدام دالة الـ getter:
"Via getter - Department: <department>" - اطبع الراتب باستخدام دالة الـ getter:
"Via getter - Salary: "Via getter - Salary: $<salary>"lt;salary>" - امنح الموظف علاوة قدرها
5000.0 - اطبع
"After raise:" - استدعِ
displayInfo()لإظهار المعلومات المحدثة
- قم بإنشاء
قم بتنسيق جميع قيم الرواتب بمنزلتين عشريتين باستخدام std::fixed و std::setprecision(2) من مكتبة <iomanip>. قم بتحويل مدخل الراتب من string إلى double باستخدام std::stod().
الفكرة الأساسية هنا هي أنه بينما يمكنك الوصول إلى name مباشرة من main، لا يمكنك الوصول إلى department أو salary مباشرة — يجب عليك استخدام دوال الـ getter العامة. هذا هو مفهوم التغليف (encapsulation) في الواقع: الفئة تتحكم في كيفية الوصول إلى بياناتها وتعديلها.
ورقة مرجعية
توفر لغة ++C ثلاثة محددات وصول (access specifiers) للتحكم في الوصول إلى أعضاء الفئة (class members):
- public: يمكن الوصول إليه من أي مكان
- protected: يمكن الوصول إليه داخل الفئة والفئات المشتقة منها
- private: يمكن الوصول إليه فقط داخل الفئة نفسها
class Account {
public:
std::string ownerName; // Accessible from anywhere
protected:
double interestRate; // Accessible in this class and derived classes
private:
double balance; // Accessible only within this class
};بشكل افتراضي، تكون جميع الأعضاء في الفئة private إذا لم يتم تحديد أي محدد وصول.
مثال يوضح التحكم في الوصول:
class Account {
private:
double balance;
public:
Account() : balance(0) {}
void deposit(double amount) {
balance += amount; // OK - inside the class
}
};
int main() {
Account acc;
acc.deposit(100); // OK - deposit is public
// acc.balance = 500; // ERROR - balance is private
}أفضل ممارسة: اجعل أعضاء البيانات (data members) خاصة (private) ووفّر طرقاً عامة (public methods) للتفاعل معها بأمان.
جرّب بنفسك
#include <iostream>
#include <string>
#include <iomanip>
#include "Employee.h"
using namespace std;
int main() {
// قراءة المدخلات
string name;
string department;
string salaryStr;
getline(cin, name);
getline(cin, department);
getline(cin, salaryStr);
double salary = stod(salaryStr);
// ضبط الدقة لمخرجات salary
cout << fixed << setprecision(2);
// TODO: إنشاء كائن Employee باستخدام قيم المدخلات
// TODO: طباعة اسم الموظف عن طريق الوصول إلى العضو العام مباشرة
// التنسيق: "Direct access - Name: <name>"
// TODO: طباعة القسم باستخدام طريقة getter
// التنسيق: "Via getter - Department: <department>"
// TODO: طباعة الراتب باستخدام طريقة getter
// التنسيق: "Via getter - Salary: $<salary>"
// TODO: منح الموظف زيادة قدرها 5000.0
// TODO: طباعة "After raise:"
// TODO: استدعاء displayInfo() لعرض المعلومات المحدثة
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مراجعة - الحاويات العامة