أعضاء الـ Instance مقابل الـ Static
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 27 من 104.
حتى الآن، كل متغير عضو قمت بإنشائه ينتمي إلى كائن محدد. عندما تقوم بإنشاء كائنين من نوع Player، يكون لكل منهما name و health الخاصين به. تُسمى هذه أعضاء النسخة (instance members) - وهي توجد بشكل منفصل لكل نسخة من الفئة (class).
ولكن ماذا لو كنت بحاجة إلى بيانات مشتركة عبر جميع الكائنات؟ على سبيل المثال، تتبع عدد اللاعبين الموجودين في المجمل.
هنا يأتي دور الأعضاء الساكنة (static members). فالعضو الساكن ينتمي إلى الفئة (class) نفسها، وليس إلى أي كائن (object) معين.
class Player {
std::string name; // عضو مثيل - كل لاعب لديه عضو خاص به
int health; // عضو مثيل
static int playerCount; // عضو ثابت - مشترك بين جميع اللاعبين
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // زيادة عند إنشاء أي لاعب
}
~Player() {
playerCount--; // إنقاص عند تدمير أي لاعب
}
};الفرق الرئيسي: يتم الوصول إلى أعضاء النسخة (instance members) من خلال كائن (object)، بينما يمكن الوصول إلى الأعضاء الساكنة (static members) من خلال اسم الفئة (class) نفسه. فكر في أعضاء النسخة على أنها "واحد لكل كائن" والأعضاء الساكنة على أنها "واحد لكل فئة".
| أعضاء النسخة (Instance Members) | الأعضاء الساكنة (Static Members) |
|---|---|
| فريدة لكل كائن | مشتركة بين جميع الكائنات |
| تُنشأ عند إنشاء الكائن | توجد قبل إنشاء أي كائن |
يتم الوصول إليها عبر الكائن: player.health | يتم الوصول إليها عبر الفئة: Player::playerCount |
تعد الأعضاء الساكنة (Static members) مفيدة للعدادات، أو قيم الإعدادات، أو أي بيانات تنتمي منطقياً إلى الفئة بدلاً من الكائنات الفردية.
التحدي
سهللنقم ببناء نظام مخزون منتجات يتتبع تفاصيل المنتجات الفردية وإحصائيات المخزون الإجمالية باستخدام الأعضاء المثيلة (instance members) والأعضاء الساكنة (static members).
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
Product.h: قم بتعريف فئةProductالتي تدير المنتجات الفردية مع تتبع البيانات على مستوى المخزون بالكامل. يجب أن تحتوي الفئة الخاصة بك على:- أعضاء مثيلة (Instance members):
name(string) وprice(double) — فريدة لكل منتج. - عضو ساكن (static member) باسم
totalProductsيقوم بعدّ عدد المنتجات الموجودة حالياً. - عضو ساكن (static member) باسم
totalValueيتتبع السعر الإجمالي لجميع المنتجات. - منشئ (constructor) يأخذ الاسم والسعر، ويقوم بتهيئة الأعضاء المثيلة، وتحديث كلا العدادين الساكنين (زيادة العدد، وإضافة السعر إلى القيمة الإجمالية).
- مدمر (destructor) يقوم بتحديث العدادات الساكنة عند إزالة منتج (إنقاص العدد، وطرح السعر من القيمة الإجمالية).
- دالة
getInfo()تعيد سلسلة نصية بالتنسيق التالي:"<name>: "<name>: $<price>"lt;price>" - دالة ساكنة (static method) باسم
getInventoryStats()تعيد سلسلة نصية:"Products: <count>, Total Value: "Products: <count>, Total Value: $<value>"lt;value>"
- أعضاء مثيلة (Instance members):
main.cpp: وضح كيف يتم مشاركة الأعضاء الساكنة بين جميع الكائنات بينما تظل الأعضاء المثيلة فريدة. اقرأ اسمين لمنتجين وأسعارهما من المدخلات (name1، price1، name2، price2 — كل منها في سطر منفصل)، ثم:- اطبع إحصائيات المخزون الأولية باستخدام اسم الفئة:
Product::getInventoryStats() - أنشئ كتلة نطاق (scope block) باستخدام الأقواس المتعرجة وداخلها أنشئ كائن
Productبالاسم والسعر الأولين. اطبع معلوماته وإحصائيات المخزون الحالية. - بعد انتهاء الكتلة (تدمير المنتج الأول)، اطبع
"After first scope:"متبوعة بإحصائيات المخزون. - أنشئ كائن
Productآخر بالاسم والسعر الثانيين. اطبع معلوماته وإحصائيات المخزون. - أنشئ كائناً ثالثاً
Productبالاسم"Bonus"والسعر5.00. اطبع إحصائيات المخزون النهائية.
- اطبع إحصائيات المخزون الأولية باستخدام اسم الفئة:
تذكر تعريف أعضائك الساكنة خارج الفئة في ملف الترويسة (بعد تعريف الفئة). قم بتنسيق الأسعار بمنزلتين عشريتين باستخدام std::fixed و std::setprecision(2) من مكتبة <iomanip>.
قم بتضمين ملف الترويسة الخاص بك في main.cpp باستخدام #include "Product.h".
ورقة مرجعية
تدعم لغة C++ نوعين من أعضاء الفئة: أعضاء النسخة (instance members) و الأعضاء الساكنة (static members).
أعضاء النسخة فريدة لكل كائن - فكل نسخة تمتلك نسختها الخاصة:
class Player {
std::string name; // كل لاعب لديه اسمه الخاص
int health; // كل لاعب لديه صحته الخاصة
};الأعضاء الساكنة مشتركة بين جميع كائنات الفئة - توجد نسخة واحدة فقط تنتمي إلى الفئة نفسها:
class Player {
static int playerCount; // مشترك بين جميع اللاعبين
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // زيادة العداد المشترك
}
~Player() {
playerCount--; // الإنقاص عند التدمير
}
};الاختلافات الرئيسية:
| أعضاء النسخة | الأعضاء الساكنة |
|---|---|
| فريدة لكل كائن | مشتركة بين جميع الكائنات |
| تُنشأ عند إنشاء الكائن | توجد قبل إنشاء أي كائن |
يتم الوصول إليها عبر الكائن: player.health | يتم الوصول إليها عبر الفئة: Player::playerCount |
يمكن استدعاء الدوال الساكنة (Static methods) باستخدام اسم الفئة، ويمكنها فقط الوصول إلى الأعضاء الساكنة:
class Player {
static int playerCount;
public:
static int getPlayerCount() {
return playerCount; // يمكنها فقط الوصول إلى الأعضاء الساكنة
}
};
// الاستدعاء باستخدام اسم الفئة
int count = Player::getPlayerCount();يجب تعريف الأعضاء الساكنة خارج الفئة (عادةً في ملف .cpp أو بعد تعريف الفئة في ملف الترويسة header):
int Player::playerCount = 0;الأعضاء الساكنة مفيدة للعدادات، أو قيم الإعدادات المشتركة، أو أي بيانات تنتمي منطقياً إلى الفئة بدلاً من الكائنات الفردية.
جرّب بنفسك
#include <iostream>
#include <string>
#include "Product.h"
using namespace std;
int main() {
// قراءة المدخلات
string name1;
double price1;
string name2;
double price2;
getline(cin, name1);
cin >> price1;
cin.ignore();
getline(cin, name2);
cin >> price2;
// TODO: اطبع إحصائيات المخزون الأولية باستخدام Product::getInventoryStats()
// TODO: قم بإنشاء نطاق (scope block) باستخدام الأقواس المتعرجة
// داخل النطاق:
// - قم بإنشاء Product باستخدام name1 و price1
// - اطبع معلوماته باستخدام getInfo()
// - اطبع إحصائيات المخزون الحالية
// TODO: بعد انتهاء النطاق، اطبع "After first scope:"
// ثم اطبع إحصائيات المخزون
// TODO: قم بإنشاء Product آخر باستخدام name2 و price2
// اطبع معلوماته وإحصائيات المخزون
// TODO: قم بإنشاء Product ثالث باسم "Bonus" وسعر 5.00
// اطبع إحصائيات المخزون النهائية
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مراجعة - الحاويات العامة