المنشئات المفوضة
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 23 من 104.
عندما يكون لديك عدة منشئات (constructors) تشترك في منطق تهيئة مشترك، يمكنك استخدام المنشئات المفوضة (delegating constructors) لتجنب تكرار الكود. يقوم المنشئ المفوض باستدعاء منشئ آخر من نفس الفئة في قائمة التهيئة الخاصة به.
class Player {
std::string name;
int health;
int level;
public:
// المنشئ الأساسي - يقوم بالعمل الفعلي
Player(std::string n, int h, int lvl)
: name(n), health(h), level(lvl) {}
// يفوض المنشئ الأساسي
Player(std::string n) : Player(n, 100, 1) {}
// المنشئ الافتراضي يفوض أيضاً
Player() : Player("Unknown") {}
};يحدث التفويض في قائمة التهيئة باستخدام اسم الفئة متبوعاً بالوسائط. عندما يتم استدعاء Player()، فإنه يفوض إلى Player(std::string)، والذي بدوره يفوض إلى المنشئ ذو المعاملات الثلاثة. تُنفذ السلسلة من المنشئ المستهدف وصولاً إلى المنشئ الأصلي.
هناك قاعدة واحدة مهمة: لا يمكن للمُنشئ المفوض (delegating constructor) أن يحتوي على أعضاء آخرين في قائمة التهيئة (initializer list) الخاصة به. يجب أن يكون التفويض هو العنصر الوحيد:
class Item {
int id;
int value;
public:
Item(int i, int v) : id(i), value(v) {}
// خطأ! لا يمكن الجمع بين التفويض وتهيئة الأعضاء
// Item(int i) : Item(i, 0), id(i) {}
// صحيح - التفويض فقط
Item(int i) : Item(i, 0) {}
};تساعد المنشئات المفوضة (Delegating constructors) في تركيز منطق التهيئة في مكان واحد، مما يجعل صيانة الكود أسهل ويقلل من احتمالية وقوع الأخطاء الناتجة عن تكرار الكود.
التحدي
سهللنقم ببناء نظام إعدادات اتصال يوضح كيف يمكن للمنشئات المفوضة (delegating constructors) القضاء على تكرار الكود من خلال ربط استدعاءات المنشئات معاً.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
Connection.h: قم بتعريف فئةConnectionالتي تمثل اتصال شبكة بخيارات تكوين متنوعة. يجب أن تحتوي الفئة الخاصة بك على:- أعضاء خاصة (Private members):
host(string)، وport(int)، وtimeout(int بالثواني)، وsecure(bool). - منشئ أساسي (primary constructor) يأخذ جميع المعاملات الأربعة ويقوم بتهيئة جميع الأعضاء باستخدام قائمة التهيئة (initializer list). يجب أن يطبع هذا المنشئ
"Connection configured: <host>:<port>". - منشئ يأخذ
hostوportفقط ويفوض المنشئ الأساسي مع جعلtimeoutافتراضياً30وsecureافتراضياًfalse. - منشئ يأخذ
hostفقط ويفوض المنشئ ذا المعاملين مع جعلportافتراضياً80. - منشئ افتراضي (default constructor) يفوض المنشئ ذا المعامل الواحد مع جعل
hostافتراضياً"localhost". - دالة
getInfo()تعيد سلسلة نصية (string) بالتنسيق التالي:"<host>:<port> (timeout: <timeout>s, secure: <yes/no>)".
- أعضاء خاصة (Private members):
main.cpp: قم بتوضيح سلسلة التفويض من خلال إنشاء اتصالات باستخدام منشئات مختلفة. اقرأ اسم المضيف (host name) ورقم المنفذ (port number) من المدخلات (كل منهما في سطر منفصل)، ثم:- أنشئ
Connectionباستخدام المنشئ الافتراضي. - أنشئ
Connectionباستخدام المضيف (host) من المدخلات فقط. - أنشئ
Connectionباستخدام كل من المضيف (host) والمنفذ (port) من المدخلات. - أنشئ
Connectionمع المضيف والمنفذ من المدخلات، وtimeoutبقيمة60، وsecureمضبوط علىtrue. - اطبع
getInfo()لكل اتصال، كل واحد في سطر منفصل.
- أنشئ
تتدفق سلسلة التفويض كالتالي: المنشئ الافتراضي يستدعي المنشئ ذا المعامل الواحد، والذي بدوره يستدعي المنشئ ذا المعاملين، والذي يستدعي المنشئ الأساسي ذا المعاملات الأربعة. هذا يعني أن رسالة "Connection configured" ستُطبع مرة واحدة لكل كائن يتم إنشاؤه، ودائماً من المنشئ الأساسي.
بالنسبة لحالة الأمان في getInfo()، اطبع "yes" إذا كان secure صحيحاً (true)، وإلا اطبع "no".
قم بتضمين ملف الرأس الخاص بك في main.cpp باستخدام #include "Connection.h".
ورقة مرجعية
يستدعي المنشئ المفوِّض (delegating constructor) منشئاً آخر من نفس الفئة في قائمة التهيئة الخاصة به لتجنب تكرار الكود:
class Player {
std::string name;
int health;
int level;
public:
// المنشئ الأساسي
Player(std::string n, int h, int lvl)
: name(n), health(h), level(lvl) {}
// يفوض إلى المنشئ الأساسي
Player(std::string n) : Player(n, 100, 1) {}
// المنشئ الافتراضي يفوض أيضاً
Player() : Player("Unknown") {}
};يحدث التفويض في قائمة التهيئة باستخدام اسم الفئة متبوعاً بالوسائط. يمكن للمنشئات أن تشكل سلسلة تفويض حيث يفوض أحدها الآخر.
قاعدة مهمة: لا يمكن للمنشئ المفوِّض أن يحتوي على أعضاء آخرين في قائمة التهيئة الخاصة به. يجب أن يكون التفويض هو العنصر الوحيد:
class Item {
int id;
int value;
public:
Item(int i, int v) : id(i), value(v) {}
// خطأ! لا يمكن الجمع بين التفويض وتهيئة الأعضاء
// Item(int i) : Item(i, 0), id(i) {}
// صحيح - تفويض فقط
Item(int i) : Item(i, 0) {}
};جرّب بنفسك
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
// قراءة المدخلات
string host;
int port;
cin >> host;
cin >> port;
// TODO: إنشاء Connection باستخدام الـ default constructor
// TODO: إنشاء Connection باستخدام الـ host فقط من المدخلات
// TODO: إنشاء Connection باستخدام كل من الـ host والـ port من المدخلات
// TODO: إنشاء Connection مع الـ host والـ port المدخلين، و timeout=60، و secure=true
// TODO: طباعة getInfo() لكل connection، واحدة في كل سطر
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مراجعة - الحاويات العامة