قوائم تهيئة المنشئ
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 22 من 104.
لقد رأيت بناء جملة النقطتين الرأسيتين في الدروس السابقة مثل Player(std::string n, int h) : name(n), health(h) {}. يُسمى هذا قائمة تهيئة الأعضاء (member initializer list)، وهي الطريقة المفضلة لتهيئة الأعضاء في لغة C++.
تظهر قائمة التهيئة بعد قائمة معاملات المنشئ، وتبدأ بنقطتين رأسيتين. يتم تهيئة كل عضو مباشرة بقيمته، مع الفصل بينها بفاصلات:
class Rectangle {
int width;
int height;
const int id;
int& reference;
public:
Rectangle(int w, int h, int i, int& r)
: width(w), height(h), id(i), reference(r) {
// جسم المنشئ (يمكن أن يكون فارغاً)
}
};لماذا نستخدم قوائم التهيئة (initializer lists) بدلاً من التعيين (assignment) في جسم المنشئ (constructor body)؟ مع التعيين، يتم إنشاء الأعضاء (members) أولاً بشكل افتراضي، ثم يتم تعيين قيم جديدة لها. مع قوائم التهيئة، يتم إنشاء الأعضاء مباشرة بالقيم الصحيحة - وهذا أكثر كفاءة ومطلوب أحياناً.
أعضاء معينة يجب أن يتم تهيئتها باستخدام القائمة:
- أعضاء
const- لا يمكن تعيين قيم لها بعد الإنشاء
- الأعضاء المرجعية (Reference members) - يجب ربطها عند التهيئة
- أعضاء بدون منشئات افتراضية (default constructors)
class Player {
const int maxHealth; // يجب استخدام قائمة التهيئة
std::string name;
public:
// هذا يعمل
Player(int max, std::string n) : maxHealth(max), name(n) {}
// هذا لن يتم تجميعه (compile):
// Player(int max) { maxHealth = max; } // خطأ!
};يتم تهيئة الأعضاء بالترتيب الذي تم التصريح به في الفئة، وليس بالترتيب الموجود في قائمة التهيئة. اكتب دائماً قائمة التهيئة الخاصة بك بترتيب التصريح لتجنب الارتباك.
التحدي
سهللنقم ببناء نظام تهيئة لمحرك ألعاب يوضح متى ولماذا تكون قوائم تهيئة الأعضاء (member initializer lists) ضرورية. ستقوم بإنشاء فئة GameConfig تحتوي على أعضاء يجب تهيئتها باستخدام صيغة قائمة التهيئة.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
GameConfig.h: قم بتعريف فئةGameConfigالتي تخزن إعدادات اللعبة. يجب أن تحتوي الفئة على:- عضو
const std::string gameName— عنوان اللعبة، والذي لا يمكن تغييره بعد الإنشاء. - عضو
const int maxPlayers— الحد الأقصى لعدد اللاعبين، وهو ثابت عند الإنشاء. - عضو
int& difficultyRef— مرجع (reference) لإعداد صعوبة خارجي. - عضوين
int screenWidthوint screenHeight— أعضاء عاديين لإعدادات العرض. - منشئ (constructor) يأخذ جميع المعلمات اللازمة ويقوم بتهيئة كل عضو باستخدام قائمة التهيئة (initializer list). يجب أن تتبع قائمة التهيئة ترتيب التصريح عن الأعضاء في الفئة.
- دالة
display()تقوم بطباعة التهيئة بهذا التنسيق:Game: <gameName> Max Players: <maxPlayers> Difficulty: <difficultyRef value> Resolution: <screenWidth>x<screenHeight>
- عضو
main.cpp: قراءة قيم التهيئة من المدخلات وإنشاء كائن GameConfig. ستحتاج إلى:- قراءة اسم اللعبة (string)، والحد الأقصى للاعبين (int)، والصعوبة (int)، والعرض (int)، والارتفاع (int) من المدخلات — كل منها في سطر منفصل.
- إنشاء متغير
intمحلي للصعوبة سيشير إليه كائن التهيئة. - إنشاء كائن
GameConfigباستخدام منشئ قائمة التهيئة. - استدعاء
display()لإظهار التهيئة الأولية. - تعديل متغير الصعوبة مباشرة (اضربه في 2).
- طباعة
"After difficulty change:". - استدعاء
display()مرة أخرى لإظهار أن المرجع يعكس القيمة المحدثة.
يسلط هذا التحدي الضوء على سبب أهمية قوائم التهيئة: أعضاء const والعضو المرجعي (reference member) لا يمكن تعيين قيم لهم داخل جسم المنشئ — يجب تهيئتهم في القائمة. كما يوضح العضو المرجعي كيف تنعكس التغييرات في المتغير الأصلي من خلال كائن التهيئة.
قم بتضمين ملف الرأس الخاص بك في main.cpp باستخدام #include "GameConfig.h".
ورقة مرجعية
تعد قائمة تهيئة الأعضاء (member initializer list) هي الطريقة المفضلة لتهيئة أعضاء الفئة في لغة C++. تظهر بعد قائمة معاملات المنشئ (constructor)، وتبدأ بنقطتين رأسيتين، حيث يتم تهيئة كل عضو مباشرة:
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {
// جسم المنشئ
}
};لماذا نستخدم قوائم التهيئة؟ يتم إنشاء الأعضاء مباشرة بالقيم الصحيحة بدلاً من إنشائها افتراضياً ثم تعيين القيم لها، مما يجعل العملية أكثر كفاءة.
مطلوبة لأعضاء معينين:
- أعضاء
const- لا يمكن تعيين قيم لها بعد الإنشاء - أعضاء المراجع (Reference members) - يجب ربطها عند التهيئة
- الأعضاء التي لا تملك منشئات افتراضية
class Player {
const int maxHealth;
int& reference;
public:
// صحيح - يستخدم قائمة التهيئة
Player(int max, int& r) : maxHealth(max), reference(r) {}
// خطأ - لا يمكن تعيين قيمة لثابت أو مرجع داخل الجسم
// Player(int max) { maxHealth = max; }
};هام: يتم تهيئة الأعضاء حسب ترتيب التصريح عنها في الفئة، وليس حسب ترتيبها في قائمة التهيئة. احرص دائماً على كتابة قائمة التهيئة بنفس ترتيب التصريح.
جرّب بنفسك
#include <iostream>
#include <string>
#include "GameConfig.h"
using namespace std;
int main() {
// قراءة قيم المدخلات
string gameName;
getline(cin, gameName);
int maxPlayers;
cin >> maxPlayers;
int difficulty;
cin >> difficulty;
int width;
cin >> width;
int height;
cin >> height;
// TODO: إنشاء كائن GameConfig باستخدام منشئ قائمة التهيئة (initializer list constructor)
// يجب أن يستقبل المنشئ: gameName، maxPlayers، difficulty (عن طريق المرجع)، width، height
// TODO: استدعاء display() لعرض الإعدادات الأولية
// TODO: تعديل متغير difficulty مباشرة (بضربه في 2)
// TODO: طباعة "After difficulty change:"
// TODO: استدعاء display() مرة أخرى لعرض الإعدادات المحدثة
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مراجعة - الحاويات العامة