لماذا نجمّع البيانات معاً
حتى الآن كان كل متغير قائماً بذاته: int هنا وstring هناك. لكن البرامج الحقيقية تتعامل مع أشياء مكوّنة من عدة أجزاء؛ فالنقطة لها x وy، والطالب له اسم وعمر ومعدل تراكمي. وتمرير هذه القيم كمتغيرات متفرقة ومنفصلة عرضةٌ للأخطاء؛ فلا شيء يربط بينها، والدالة التي تحتاجها جميعاً يجب أن تأخذ الوسائط الثلاثة كلها.
تحلّ البنية هذه المشكلة. فهي تعرّف نوعاً جديداً يجمّع المتغيرات المترابطة — أي أعضاءها — في وحدة واحدة. وبمجرد تعريفها، تتعامل مع الحزمة كاملةً كقيمة واحدة يمكنك تخزينها ونسخها وتمريرها إلى الدوال.
تصل إلى كل عضو عبر عامل النقطة (s.name وs.age). والفاصلة المنقوطة بعد القوس المعقوف الختامي } لتعريف البنية إلزامية؛ وإغفالها من أكثر أخطاء الترجمة شيوعاً لدى المبتدئين في C++.
تهيئة البنية
إسناد كل حقل يدوياً يعمل، لكنه مُطنِب ومن السهل نسيان عضو ما. الخيار الأنظف هو التهيئة الإجمالية: اسرد القيم بين أقواس معقوفة بالترتيب نفسه الذي صُرِّح به الأعضاء.
احذر الحالة الافتراضية الفارغة: Point p; (بلا أقواس) يترك x وy يحملان قيماً مهملة، لأن أعضاء الأنواع المُضمَّنة لا تُصفَّر تلقائياً. أما Point p{}; فيهيّئهما بالقيمة إلى 0. فضّل الأقواس المعقوفة. ويمكنك أيضاً تضمين القيم الافتراضية مباشرةً في التعريف، فيبدأ حتى Point p; نظيفاً:
البُنى كوسائط للدوال
البنية قيمة واحدة، لذا يمكن للدالة أن تأخذ وسيطاً واحداً بدلاً من ثلاثة. تذكّر فقط أن تمرير البنية بالقيمة ينسخ كل عضو. ولأي شيء أكبر من بضعة أعداد صحيحة، مرّره بمرجع const لتجنّب النسخ — وهي القاعدة نفسها التي رأيتها في صفحة المراجع.
إعادة بنية كاملة من دالة هي الأسلوب الاصطلاحي لإرجاع عدة قيم دفعة واحدة — أنظف بكثير من التعامل مع عدة مراجع خَرْج.
إضافة دوال أعضاء وبانيات
البنية لا تقتصر على البيانات. فيمكنها أن تحتوي دوال أعضاء تعمل على أعضائها هي، وبانياً يهيّئ الكائن لحظة إنشائه. وهنا تبدأ البنية بالظهور كأنها كائن صغير ذو سلوك.
داخل دالة العضو، يُوصَل إلى الأعضاء بالاسم (width وheight)؛ فهي تشير إلى نسخة هذا الكائن. ووسم area() const يخبر المترجم أن الدالة تقرأ الكائن فقط، ما يتيح استدعاءها أيضاً على قيم const Rectangle. والبانيات موضوع قائم بذاته، بما في ذلك صيغة قائمة التهيئة : width(w) — وصفحة البانيات تتعمّق في ذلك.
struct مقابل class: الفرق الحقيقي
ربما سمعت أن «البُنى للبيانات والأصناف للكائنات». هذا عُرف لا قاعدة في اللغة. ففي C++ تكاد struct وclass تكونان الشيء نفسه — والفرق المُضمَّن الوحيد هو مستوى الوصول الافتراضي.
struct S {
int x; // public افتراضياً
};
class C {
int x; // private افتراضياً
};
أعضاء struct تكون public ما لم تَسِمها بخلاف ذلك؛ وأعضاء class تبدأ private. هذا كل شيء — كلاهما يمكن أن يملك بانيات ودوال أعضاء ووراثة وكل شيء آخر. بل يمكنك أن تضيف إلى البنية محدّدات وصول صريحة لإخفاء أعضاء:
الخلاصة العملية: استخدم struct لتجميعات البيانات الشفافة التي يُقصد التعامل مع كل حقل فيها بحرية (مثل Point وColor وسجل إعدادات)، والجأ إلى class حين تريد حماية الحالة الداخلية خلف واجهة عامة. والمترجم يعاملهما على حد سواء؛ والكلمة المفتاحية تشير فقط إلى قصدك.
مصفوفات ومتجهات من البُنى
لأن البنية نوع عادي، يمكنك وضع كثير منها في مصفوفة أو في متجه (vector) والمرور عليها بحلقة كأي قيمة أخرى.
لاحظ الأقواس المتداخلة: كل {"Keyboard", 49.99} يهيّئ Product واحداً تهيئةً إجمالية، والأقواس الخارجية تبني المتجه. واستخدام const Product& في الحلقة القائمة على المدى يتجنب نسخ كل بنية في كل تكرار — وإن حذفت & فستُكرّر كل عنصر بلا داعٍ.
التالي: التعدادات (Enums)
تتيح لك البُنى بناء نوع من عدة قيم مجموعة معاً. أما الفصل التالي فيسلك الاتجاه المعاكس: التعداد (enum) يعرّف نوعاً يمكنه أن يحمل قيمة واحدة بالضبط من مجموعة صغيرة مسمّاة — وهو مثالي لأشياء مثل Color::Red وDirection::North أو حالة آلة الحالات. وسترى كيف يمنحك enum class ثوابت مقروءة وآمنة من حيث النوع تتلاءم طبيعياً مع البُنى والأصناف التي تبنيها الآن.
الأسئلة الشائعة
ما هي البنية (struct) في C++؟
البنية struct هي نوع يعرّفه المستخدم ويجمّع عدة متغيرات مترابطة (تُسمى أعضاء) تحت اسم واحد. فبدلاً من التعامل مع متغيرات منفصلة مثل string name وint age وdouble gpa، تجمعها في نوع واحد Student وتمرّر هذا الكائن الواحد. كما يمكن للبنية أن تحتوي على دوال أعضاء وبانيات؛ ففي C++ الحديثة هي صنف كامل أعضاؤه public افتراضياً.
ما الفرق بين struct وclass في C++؟
تقنياً، مستوى الوصول الافتراضي فقط: أعضاء struct تكون public ما لم تذكر خلاف ذلك، بينما أعضاء class تكون private افتراضياً. وينطبق الأمر نفسه على الوراثة. وكل ما عدا ذلك (البانيات، دوال الأعضاء، الطرق، الوراثة) يعمل بشكل متطابق. وبحكم العُرف، يلجأ المبرمجون إلى struct لتجميعات البيانات البسيطة، وإلى class للأنواع ذات الثوابت المحفوظة والتفاصيل الداخلية المخفية.
كيف تهيّئ بنية في C++؟
أبسط طريقة هي التهيئة الإجمالية (aggregate initialization) بالأقواس المعقوفة وبترتيب الأعضاء: Point p{3, 4};. ويمكنك أيضاً إسناد كل حقل يدوياً عبر عامل النقطة (p.x = 3;)، أو منح الأعضاء قيماً افتراضية في التعريف (int x = 0;)، أو كتابة بانٍ كي تهيّئ البنية نفسها. والتهيئة بالأقواس المعقوفة مفضّلة لأنها لا تترك حقولاً غير مهيّأة بصمت.