تحميل العوامل [] و ()
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 45 من 104.
يسمح عامل الفهرسة ([]) لفئتك بالتصرف مثل المصفوفة، مما يوفر وصولاً مفهرساً للعناصر. كما يحول عامل استدعاء الدالة (()) الكائنات إلى كيانات قابلة للاستدعاء، تُعرف باسم functors.
إليك غلاف مصفوفة بسيط مع عامل الفهرسة (subscript operator):
class IntArray {
int* data;
size_t size;
public:
IntArray(size_t s) : size(s), data(new int[s]()) {}
~IntArray() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
const int& operator[](size_t index) const {
return data[index];
}
};لاحظ أننا نوفر نسختين: إحداهما تعيد مرجعاً غير ثابت (non-const reference) (مما يسمح بالتعديل)، والنسخة الثابتة (const) تعمل مع الكائنات الثابتة (const objects). هذا يتيح لك كتابة arr[0] = 5; للقراءة والكتابة.
يُنشئ عامل استدعاء الدالة (function call operator) كائنات يمكن "استدعاؤها" مثل الدوال:
class Multiplier {
int factor;
public:
Multiplier(int f) : factor(f) {}
int operator()(int value) const {
return value * factor;
}
};
Multiplier triple(3);
int result = triple(10); // يعيد 30تعتبر الفانكتورز (Functors) قوية لأنها تستطيع الحفاظ على الحالة بين الاستدعاءات، على عكس الدوال العادية. وهي تُستخدم بشكل شائع مع خوارزميات STL حيث تحتاج إلى سلوك قابل للتخصيص يتذكر قيم الإعدادات.
التحدي
سهللنقم ببناء فئة ScoreTracker تجمع بين عاملي الـ subscript (الوصول عبر الفهرس) و function call (استدعاء الدالة). ستقوم هذه الفئة بتخزين درجات اللاعبين وتسمح لك بالوصول إليها عن طريق الفهرس باستخدام []، بينما تعمل أيضاً كـ functor يطبق مضاعف مكافأة (bonus multiplier) على أي درجة تمررها إليها.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
ScoreTracker.h: قم بتعريف فئةScoreTrackerالتي تدير مصفوفة من درجات اللاعبين ويمكنها تطبيق المكافآت. يجب أن تحتوي الفئة الخاصة بك على:- مصفوفة
int*مخصصة ديناميكياً لتخزين الدرجات - عضو من نوع
size_tلعدد الدرجات - عضو من نوع
doubleلمضاعف المكافأة - منشئ (constructor) يأخذ الحجم ومضاعف المكافأة، ويقوم بتهيئة جميع الدرجات إلى 0
- مدمر (destructor) يقوم بتنظيف الذاكرة الديناميكية بشكل صحيح
- عامل الـ subscript
[](بإصداري const و non-const) للوصول إلى الدرجات عن طريق الفهرس - عامل استدعاء الدالة
()الذي يأخذ درجة من نوعintويعيد الدرجة مضروبة في مضاعف المكافأة (كـint) - طريقة
getSize()تعيد عدد الدرجات
يعيد عامل الـ subscript غير الثابت (non-const) مرجعاً بحيث يمكنك كتابة
tracker[0] = 100;. بينما يسمح إصدار const بالوصول للقراءة فقط للكائنات الثابتة. يجب تمييز عامل استدعاء الدالة بـconstلأنه لا يعدل حالة الكائن.- مصفوفة
main.cpp: اقرأ المدخلات التالية (كل منها في سطر منفصل):- عدد اللاعبين (الحجم)
- مضاعف المكافأة (كـ double)
- درجات كل لاعب (واحدة في كل سطر، بعدد الحجم)
- درجة اختبار لتطبيق المكافأة عليها
قم بإنشاء
ScoreTracker، واستخدم عامل الـ subscript لتخزين درجة كل لاعب، ثم اعرض جميع الدرجات. أخيراً، استخدم عامل استدعاء الدالة لحساب الدرجة بعد تطبيق المكافأة.تنسيق المخرجات:
Player 0: <score> Player 1: <score> ... Bonus applied to <test_score>: <result>
على سبيل المثال، مع 3 لاعبين، ومضاعف 1.5، ودرجات 100، 200، 150، ودرجة اختبار 80:
Player 0: 100
Player 1: 200
Player 2: 150
Bonus applied to 80: 120استخدم std::stoi() و std::stod() لتحويل المدخلات. لا تنسَ حواجز التضمين (header guards) في ملف الترويسة الخاص بك.
ورقة مرجعية
يسمح عامل الفهرسة [] بالوصول إلى الكائنات مثل المصفوفات. يجعل عامل استدعاء الدالة () الكائنات قابلة للاستدعاء مثل الدوال (الكائنات الدالية).
عامل الفهرسة
توفير نسختين ثابتة (const) وغير ثابتة (non-const) للوصول للقراءة والكتابة:
class IntArray {
int* data;
size_t size;
public:
IntArray(size_t s) : size(s), data(new int[s]()) {}
~IntArray() { delete[] data; }
// نسخة غير ثابتة: تسمح بالتعديل
int& operator[](size_t index) {
return data[index];
}
// نسخة ثابتة: وصول للقراءة فقط
const int& operator[](size_t index) const {
return data[index];
}
};عامل استدعاء الدالة
ينشئ كائنات دالية (functors) يمكنها الحفاظ على الحالة بين الاستدعاءات:
class Multiplier {
int factor;
public:
Multiplier(int f) : factor(f) {}
int operator()(int value) const {
return value * factor;
}
};
Multiplier triple(3);
int result = triple(10); // يعيد 30الكائنات الدالية مفيدة مع خوارزميات STL حيث تكون هناك حاجة لسلوك قابل للتخصيص مع إعدادات مخزنة.
جرّب بنفسك
#include <iostream>
#include <string>
#include "ScoreTracker.h"
using namespace std;
// TODO: تنفيذ منشئ ScoreTracker
ScoreTracker::ScoreTracker(size_t size, double multiplier) {
// TODO: تهيئة الأعضاء وتخصيص مصفوفة ديناميكية
// تهيئة جميع الدرجات إلى 0
}
// TODO: تنفيذ مدمر ScoreTracker
ScoreTracker::~ScoreTracker() {
// TODO: تنظيف الذاكرة الديناميكية
}
// TODO: تنفيذ عامل الفهرسة (subscript operator) غير الثابت (non-const)
// TODO: تنفيذ عامل الفهرسة (subscript operator) الثابت (const)
// TODO: تنفيذ عامل استدعاء الدالة (function call operator)
// TODO: تنفيذ تابع getSize
size_t ScoreTracker::getSize() const {
// TODO: إرجاع عدد الدرجات
}
int main() {
string line;
// قراءة عدد اللاعبين
getline(cin, line);
size_t numPlayers = stoi(line);
// قراءة مُعامل المكافأة (bonus multiplier)
getline(cin, line);
double multiplier = stod(line);
// TODO: إنشاء ScoreTracker باستخدام numPlayers و multiplier
// TODO: قراءة الدرجات لكل لاعب وتخزينها باستخدام عامل الفهرسة (subscript operator)
// TODO: قراءة درجة الاختبار
// TODO: عرض جميع درجات اللاعبين بالتنسيق: "Player X: <score>"
// TODO: عرض نتيجة تطبيق المكافأة بالتنسيق: "Bonus applied to <test_score>: <result>"
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مراجعة - الحاويات العامة