فئة أم بنية في C++: الجدل الأبدي
\n\nتخيل موقفًا: أنت تكتب كودًا بلغة C++، وتحتاج إلى إنشاء نوع بيانات جديد. يتبادر إلى ذهنك على الفور سؤال: «متى يجب استخدام class، ومتى يجب استخدام struct؟». هذا السؤال لا يقلق المبتدئين فقط - بل حصد آلاف المشاهدات وعشرات المناقشات الساخنة على Stack Overflow. وهذا ليس مفاجئًا: ففي C++، هاتان الكلمتان الرئيسيتان تقومان بنفس الشيء للوهلة الأولى، ولكن مع فروق دقيقة.
\n\nفي هذه المقالة، سنحسم الأمر مرة واحدة وإلى الأبد، ونفهم الفرق الحقيقي بين class و struct، ومتى نختار أيهما، وكيف لا نقع في الخطأ. هيا بنا!
الفرق الرئيسي: معدّلات الوصول الافتراضية
\n\nإذا كنت تعتقد أن class و struct هما كيانان مختلفان تمامًا، فأنت مخطئ. في C++، الاختلاف الجوهري الوحيد بينهما هو معدّل الوصول الافتراضي:
- \n
- struct: جميع الأعضاء (الحقول والدوال) تكون افتراضيًا
public. \n - class: جميع الأعضاء تكون افتراضيًا
private. \n
هذا كل شيء. لا توجد أي اختلافات أخرى على مستوى اللغة. لا أداء ولا سحر. فقط قابلية الوصول. دعنا نرى كيف يعمل ذلك من خلال الأمثلة.
\n\nمثال مع struct
\nstruct Point {\n int x;\n int y;\n};\n\n// يمكن الوصول مباشرة\nPoint p;\np.x = 10;\np.y = 20;\n\nمثال مع class
\nclass Point {\n int x; // private افتراضيًا!\n int y;\npublic:\n void setX(int val) { x = val; }\n void setY(int val) { y = val; }\n};\n\nPoint p;\n// p.x = 10; // خطأ! x خاص (private)\np.setX(10); // هذا مسموح به\n\nكما ترى، الفرق فقط في كيفية إخفاء البيانات «افتراضيًا». لكن هذا لا يعني أن الاختيار بين class و struct هو مسألة ذوق. هناك ممارسات متعارف عليها.
\n\nمتى تختار struct؟
\n\nفي مجتمع C++، هناك إجماع غير مكتوب: struct - للبيانات البسيطة. استخدم struct عندما يكون النوع الخاص بك مجرد مجموعة من الحقول العامة بدون منطق معقد. الأمثلة الكلاسيكية:
\n\n- \n
- إحداثيات نقطة (
x, y) \n - لون RGB (
r, g, b) \n - إعدادات (
width, height, title) \n - نتيجة عملية (
success, errorMessage) \n
في مثل هذه الحالات، لا داعي لإخفاء البيانات - فهي عامة بطبيعتها. struct يجعل الكود أنظف وأقصر. بالإضافة إلى ذلك، struct مناسب تمامًا لـ أنواع POD (Plain Old Data) - الهياكل التي يمكن نسخها باستخدام memcpy بسيط.
مثال على الاستخدام الصحيح لـ struct
\nstruct Vector2 {\n float x;\n float y;\n \n // يمكن إضافة دوال بسيطة\n float length() const {\n return std::sqrt(x*x + y*y);\n }\n};\n\n// نستخدمه كحاوية بيانات بسيطة\nVector2 v = {3.0f, 4.0f};\nstd::cout << v.length(); // 5.0\n\nلاحظ: حتى إذا كان struct يحتوي على دوال، فهذا أمر طبيعي. المهم أن تبقى الحقول عامة، ولا يتطلب النوع منطقًا معقدًا للحفاظ على الثوابت (invariants).
متى تختار class؟
\n\nClass هو الخيار الافتراضي للكائنات الكاملة. استخدم class عندما:
- \n
- لديك منطق معقد يتطلب التغليف (encapsulation). \n
- تريد التحكم الصارم في كيفية تغيير البيانات. \n
- النوع الخاص بك يحتوي على ثوابت (invariants) (مثل «العمر لا يمكن أن يكون سالبًا»). \n
- تستخدم الوراثة، الدوال الافتراضية، تعدد الأشكال. \n
المثال الكلاسيكي هو فئة «حساب بنكي». لا ينبغي أن يكون الرصيد عامًا، وإلا فقد يقوم شخص ما بتعيين قيمة سالبة عن طريق الخطأ.
\n\nمثال على الاستخدام الصحيح لـ class
\nclass BankAccount {\nprivate:\n double balance;\n std::string owner;\n \npublic:\n BankAccount(const std::stri