التعدادات النطاقية في C++: لماذا enum class أكثر أمانًا من enum القديم؟

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا enum class هو صديقك الجديد الأفضل في C++؟



كل من بدأ في تعلم C++ أو عاد إليها بعد انقطاع، يواجه عاجلاً أم آجلاً معضلة: استخدام enum القديم الجيد أم نظيره الأحدث enum class؟ على Stack Overflow، جمع سؤال «لماذا يعتبر enum class أكثر أمانًا من plain enum؟» آلاف المشاهدات - وهذا ليس من قبيل الصدفة. الانتقال إلى scoped enum (هذا هو الاسم الرسمي لـ enum class) ليس مجرد موضة، بل خطوة حقيقية نحو كود أكثر موثوقية وقابلية للقراءة.



دعنا نفهم بالضبط ما يكمن من خطر في enum القديم وكيف يساعد enum class في تجنب فئة كاملة من الأخطاء. إذا كنت تكتب بلغة C++ وما زلت تستخدم enum بدون نطاق - فهذه المقالة لك.



أهم 3 أخطاء في enum القديم (وكيف يصلحها enum class)



1️⃣ تلويث النطاق العام



المشكلة الأكثر شيوعًا في enum القديم هي أن ثوابته "تتسرب" إلى مساحة الأسماء المحيطة. تخيل أن لديك تعدادين:



// سيء: enum قديمenum Color { Red, Green, Blue };enum Feeling { Happy, Sad, Blue }; // خطأ في الترجمة! Blue مُعرّف مسبقًا


الثابت Blue من Color مشغول بالفعل، ولا يمكنك استخدام نفس الاسم في Feeling. في مشروع كبير بمئات الملفات، يؤدي هذا إلى صداع حقيقي. تضطر لابتكار أسماء طويلة مثل Color_Blue أو COLOR_BLUE، مما يشوه الكود.



الحل مع enum class:



// جيد: enum class (تعداد نطاقي)enum class Color { Red, Green, Blue };enum class Feeling { Happy, Sad, Blue }; // لا بأس! لا يوجد تعارض


الآن Blue يعيش داخل صنفه الخاص. للوصول إليه، يجب كتابة Color::Blue أو Feeling::Blue. هذا لا يحل مشكلة التصادمات فحسب، بل يجعل الكود موثقًا ذاتيًا - يتضح فورًا أي نوع ينتمي إليه القيمة.



2️⃣ التحويل الضمني إلى int (والعكس)



الـ enum القديم يتحول ضمنيًا إلى الأنواع الصحيحة. للوهلة الأولى، هذا مريح، لكنه عمليًا مصدر لأخطاء يصعب اكتشافها.



// enum قديم خطيرenum WarningLevel { Low, Medium, High };

void setVolume(int level) { if (level > 10) level = 10; // ...}

setVolume(High); // يعمل! لكن High = 2، وليس مستوى الصوت// لقد أرسلت مستوى تحذير بدلاً من مستوى الصوت - المترجم صامت


المترجم سيقبل هذا دون أي تحذير. وإذا خلطت ترتيب الوسائط في دالة - سيذهب الخطأ إلى وقت التشغيل.



كيف يحمي enum class:



enum class WarningLevel { Low, Medium, High };

void setVolume(int level);

setVolume(WarningLevel::High); // خطأ في الترجمة!// لا يوجد تحويل ضمني من enum class إلى int


المترجم سيجبرك على تحويل القيمة بشكل صريح إذا كان ذلك ضروريًا حقًا. وهذا يعني أنك تدرك كل تحويل، بدلاً من الاعتماد على "ربما سينجح".



// إذا كان ضروريًا جدًا - نفعله بشكل صريحsetVolume(static_cast<int>(WarningLevel::High)); // صريح وواضح


3️⃣ النوع الأساسي غير المحدد



الـ enum القديم لا يضمن أي نوع صحيح سيستخدم تحت الغطاء. المترجم يختاره بنفسه (عادةً int)، وهذا قد يؤدي إلى مفاجآت عند النقل بين مترجمات أو منصات مختلفة.



// الحجم قد يكون مختلفًا!enum Small { A, B, C }; // قد يكون int، قد يكون charenum Big { X = 100000, Y, Z }; // لا يزال قد يكون int


مع enum class، يمكنك تحديد النوع الأساسي بشكل صريح، مما يمنحك تحكمًا كاملاً

المدونات

توصيات الكتب