static_cast، التحويل على غرار C والترميز الوظيفي: معركة العمالقة في C++
\n\nمرحباً، زميلي المبرمج! إذا كنت قد كتبت أي كود بلغة C++، فبالتأكيد سألت نفسك: "لماذا يصر كل هؤلاء الخبراء على استخدام static_cast<int>(x) بدلاً من (int)x أو int(x) القديم الجيد؟ فالنتيجة واحدة، أليس كذلك؟"
المفسد: النتيجة ليست دائماً واحدة، والفرق قد يكلفك ساعات من التصحيح، وأحياناً سلامة البرنامج. في هذه المقالة، سنحلل سؤالاً شائعاً من Stack Overflow (حصد آلاف المشاهدات) ونشرح بالتفصيل لماذا توصي لغة C++ الحديثة (بدءاً من معيار C++11 فصاعداً) بشدة باستخدام static_cast.
\n\nلن نكتفي بسرد القواعد، بل سنعرضها بأمثلة حية يمكنك تشغيلها. هيا بنا!
\n\n1. "الضوضاء" البصرية والبحث في الكود
\n\nمشكلة التحويل على غرار C
\n\nتخيل سطر كود:
\n\nint result = (int)someDoubleValue * (int)anotherDoubleValue;\n\nكم عدد التحويلات هنا؟ اثنان. لكن ابحث عنهما بعينيك في نصف ثانية. تخيل الآن أن لديك 5000 سطر في الكود، ونصفها يحتوي على هذه الأقواس. ستكون جحيماً حقيقياً في مراجعة الكود.
\n\nالآن انظر إلى نفس الكود مع static_cast:
int result = static_cast<int>(someDoubleValue) * static_cast<int>(anotherDoubleValue);\n\nيتضح فوراً: "آه، هنا نقوم عمداً بتحويل double إلى int". يعمل static_cast كتعليق توثيقي. يصرخ: "أنا أعرف ما أفعله! هذا تحويل نوع واعٍ!"
\n\nمكافأة لـ grep وبيئة التطوير المتكاملة
\n\nحاول العثور على جميع الأماكن في المشروع التي تستخدم التحويل على غرار C. ستبحث ببساطة عن ) أو ( — وهذا مستحيل. أما static_cast فيمكن البحث عنه بأمر واحد grep -r "static_cast". ستقوم بيئة التطوير المتكاملة بتسليط الضوء عليها، ويمكنك المرور بسرعة على جميع الأماكن الخطرة.
2. الأمان: المترجم صديقك وليس عدوك
\n\nخطأ مبتدئ نموذجي
\n\nلنفترض أنك تريد تحويل مؤشر لفئة أساسية إلى مؤشر لفئة مشتقة. التحويل على غرار C سيفعل ذلك بصمت، حتى لو لم تكن الفئات مرتبطة:
\n\nclass Base { /* ... */ };\nclass Derived : public Base { /* ... */ };\nclass Unrelated { /* ... */ };\n\nBase* basePtr = new Derived;\nUnrelated* badPtr = (Unrelated*)basePtr; // المترجم سيسكت!\n// الآن لديك سلوك غير محدد عند الوصول إلى badPtr\n\n\nهذا يسمى reinterpret_cast، والتحويل على غرار C ينفذه دون تحذير. static_cast في هذه الحالة سيصدر خطأ في الترجمة:
Unrelated* badPtr = static_cast<Unrelated*>(basePtr); // خطأ في الترجمة!\n// error: invalid static_cast from type 'Base*' to type 'Unrelated*'\n\n\nالمترجم هو درعك. التحويل على غرار C يشبه إيقاف تشغيل جميع أنظمة الأمان في السيارة والأمل في ألا تصطدم.
\n\nتحويل المؤشرات لأسفل التسلسل الهرمي (downcast)
\n\nالتحويل على غرار C يسمح بالتحويل لأسفل دون التحقق من النوع أثناء التنفيذ. static_cast يسمح بذلك أيضاً، لكنه على الأقل يتحقق من أن الفئات في نفس التسلسل الهرمي للوراثة. للتحويل الآمن لأسفل مع التحقق من RTTI، استخدم dynamic_cast — لكن هذه قصة أخرى.
3. التحكم فيما تفعله
\n\nثلاثة تحويلات مختلفة في قوس واحد
\n\nالتحويل على غرار C هو سكين سويسري يمكنه فعل ثلاثة أشياء مختلفة:
\n- \n
- static_cast (تحويل عادي بين الأنواع الرقمية أو لأعلى/لأسفل التسلسل الهرمي) \n
- const_cast (إزالة الثبات) \n
- reinterpret_cast (إعادة تفسير التمثيل الثنائي) \n
هذا مثال يصدم الكثيرين:
\n\nconst