الكلاسات في C++: البرمجة كائنية التوجه
الفرق الأكبر بين C و C++ هو البرمجة كائنية التوجه (Object-Oriented Programming). الاسم الأول للغة كان «C with Classes»، أي «C مع الكلاسات». الكلاسات تسمح لك بجمع البيانات والدوال التي تعمل عليها في وحدة واحدة، وهذا ما يجعل برامج ضخمة مثل الألعاب والمتصفحات منظمة وقابلة للتطوير.
أول كلاس
Section titled “أول كلاس”class Student {private: string name; int age;
public: Student(string n, int a) : name(n), age(a) {}
void introduce() const { cout << "أنا " << name << " وعمري " << age << " سنة" << endl; }};
int main() { Student s1("سارة", 14); Student s2("عمر", 15); s1.introduce(); s2.introduce(); return 0;}أنا سارة وعمري 14 سنةأنا عمر وعمري 15 سنة- الكلاس قالب، والكائن نسخة حقيقية منه:
s1وs2كائنان، لكل منهما بياناته. private:الأعضاء بعدها لا يمكن الوصول إليها من خارج الكلاس.public:الأعضاء بعدها متاحة للجميع.- لاحظ الفاصلة المنقوطة بعد القوس الأخير للكلاس
};، ونسيانها خطأ شائع جداً.
المُنشئ وقائمة التهيئة
Section titled “المُنشئ وقائمة التهيئة”Student(string n, int a) : name(n), age(a) {}المُنشئ (Constructor) دالة اسمها نفس اسم الكلاس وبلا نوع مرجع، وتُنفَّذ عند إنشاء الكائن. والجزء بعد النقطتين : name(n), age(a) يسمى قائمة التهيئة (Initializer List)، ويعطي الحقول قيمها الأولى مباشرة. وهي الطريقة المفضلة في C++، والقوسان المعقوفان {} بعدها فارغان لأنه لا يوجد عمل إضافي.
الدوال الثابتة: const
Section titled “الدوال الثابتة: const”لاحظ const بعد introduce(). معناها: «هذه الدالة لا تغيّر بيانات الكائن». المترجم يمنعها من تعديل أي حقل، ويسمح باستدعائها على الكائنات الثابتة. اجعل كل دالة تقرأ فقط const، فهذه عادة احترافية في C++.
التغليف: getters و setters
Section titled “التغليف: getters و setters”int getAge() const { return age; }
void setAge(int a) { if (a < 0) { cout << "العمر لا يمكن أن يكون سالباً" << endl; return; } age = a;}s2.setAge(16);s2.setAge(-3);cout << s2.getAge() << endl;العمر لا يمكن أن يكون سالباً16الحقل age خاص، والتعديل يمر فقط عبر setAge التي تتحقق من القيمة. هذا هو التغليف (Encapsulation): مثل لوحة التحكم في السيارة، تقود عبر المقود والدواسات، ولا تعبث بالمحرك مباشرة.
struct: كلاس للبيانات البسيطة
Section titled “struct: كلاس للبيانات البسيطة”struct Point { int x; int y;};
Point p{3, 4};cout << p.x << ", " << p.y << endl;3, 4struct في C++ مثل class تماماً، والفرق الوحيد أن أعضاءه عامة افتراضياً. والعُرف: استخدم struct للبيانات البسيطة بلا قواعد خاصة، و class عندما تحتاج حماية البيانات وسلوكاً.
الوراثة و virtual
Section titled “الوراثة و virtual”class Animal {protected: string name;
public: Animal(string n) : name(n) {} virtual ~Animal() = default;
virtual void speak() const { cout << name << " يصدر صوتاً" << endl; }};
class Cat : public Animal {public: Cat(string n) : Animal(n) {}
void speak() const override { cout << name << " يقول: مياو" << endl; }};class Cat : public Animal: القطة ترث من الحيوان.Animal(n)في قائمة التهيئة: استدعاء مُنشئ الأب.protected: مثلprivate، لكن الكلاسات الوارثة تستطيع الوصول.virtual: تسمح للكلاسات الوارثة بإعادة تعريف الدالة، بحيث يُستدعى السلوك الصحيح حسب نوع الكائن الحقيقي.override: تؤكد للمترجم أنك تعيد تعريف دالةvirtual، فينبهك إذا أخطأت في اسمها أو معاملاتها.virtual ~Animal() = default;: المُدمِّر (Destructor) الافتراضي. أي كلاس فيه دوالvirtualيجب أن يكون مدمّرهvirtualأيضاً، لتُحذف الكائنات الوارثة بشكل صحيح.
واستخدامها معاً:
#include <memory>
vector<unique_ptr<Animal>> animals;animals.push_back(make_unique<Animal>("الحصان"));animals.push_back(make_unique<Cat>("مشمش"));for (const auto& a : animals) { a->speak();}الحصان يصدر صوتاًمشمش يقول: مياوunique_ptr مؤشر ذكي: يحمل الكائن ويحذفه من الذاكرة تلقائياً عندما لا يعود مطلوباً، فلا تحتاج new و delete اليدوية التي سببت أخطاء كثيرة في الماضي. و -> تستدعي الدالة عبر المؤشر. والنتيجة: قائمة واحدة فيها أنواع مختلفة، وكل كائن يتصرف حسب نوعه الحقيقي. هذه الميزة اسمها تعدد الأشكال (Polymorphism).
جرّب بنفسك
Section titled “جرّب بنفسك”- صمم كلاس
BankAccountبحقل خاصbalance، ودالتينdepositوwithdrawترفضان المبالغ غير المنطقية، ودالةgetBalance() const. - أنشئ حسابين، ونفذ عمليات، واطبع الأرصدة.
- أنشئ
struct Productفيه الاسم والسعر، وvector<Product>من 3 منتجات، واطبع إجمالي الأسعار. - تحدٍّ: أنشئ
class Shapeبدالةvirtual double area() const، وكلاسينCircleوRectangleيعيدان تعريفها، وضع أشكالاً فيvector<unique_ptr<Shape>>واطبع مساحاتها.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان الفاصلة المنقوطة بعد الكلاس:
};مطلوبة، وغيابها يعطي أخطاء محيرة في أسطر بعيدة. - نسيان virtual في الأب: تُستدعى دالة الأب دائماً بدلاً من دالة الابن عند التعامل عبر مؤشر للأب.
- الوصول لحقل private من الخارج: خطأ
is private within this context. استخدم دالة عامة.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما الفرق الوحيد بين struct و class في C++؟
الاثنان متطابقان تقريباً، والفرق في مستوى الوصول الافتراضي للأعضاء.
سؤال
لماذا نكتب virtual قبل دالة speak في الكلاس الأب؟
virtual تجعل الاستدعاء يعتمد على نوع الكائن الحقيقي، وهذا أساس تعدد الأشكال.
الخلاصة
Section titled “الخلاصة”- الكلاس قالب يجمع البيانات والدوال، و
privateوpublicتحددان الوصول. - المُنشئ بقائمة التهيئة
: name(n)، والدوال التي تقرأ فقط تُعلَّم بـconst. - التغليف: حقول خاصة مع دوال تتحقق من البيانات.
structللبيانات البسيطة، والوراثة بـ: public Baseمعvirtualوoverride.unique_ptrوmake_uniqueلإدارة الذاكرة تلقائياً.