static_cast مقابل التحويل على غرار C في C++: أيهما تختار ولماذا؟

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

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

ابدأ الدورة

static_cast، التحويل على غرار C والترميز الوظيفي: معركة العمالقة في C++

\n\n

مرحباً، زميلي المبرمج! إذا كنت قد كتبت أي كود بلغة C++، فبالتأكيد سألت نفسك: "لماذا يصر كل هؤلاء الخبراء على استخدام static_cast<int>(x) بدلاً من (int)x أو int(x) القديم الجيد؟ فالنتيجة واحدة، أليس كذلك؟"

\n\n

المفسد: النتيجة ليست دائماً واحدة، والفرق قد يكلفك ساعات من التصحيح، وأحياناً سلامة البرنامج. في هذه المقالة، سنحلل سؤالاً شائعاً من Stack Overflow (حصد آلاف المشاهدات) ونشرح بالتفصيل لماذا توصي لغة C++ الحديثة (بدءاً من معيار C++11 فصاعداً) بشدة باستخدام static_cast.

\n\n

لن نكتفي بسرد القواعد، بل سنعرضها بأمثلة حية يمكنك تشغيلها. هيا بنا!

\n\n

1. "الضوضاء" البصرية والبحث في الكود

\n\n

مشكلة التحويل على غرار C

\n\n

تخيل سطر كود:

\n\n
int result = (int)someDoubleValue * (int)anotherDoubleValue;
\n\n

كم عدد التحويلات هنا؟ اثنان. لكن ابحث عنهما بعينيك في نصف ثانية. تخيل الآن أن لديك 5000 سطر في الكود، ونصفها يحتوي على هذه الأقواس. ستكون جحيماً حقيقياً في مراجعة الكود.

\n\n

الآن انظر إلى نفس الكود مع static_cast:

\n\n
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". ستقوم بيئة التطوير المتكاملة بتسليط الضوء عليها، ويمكنك المرور بسرعة على جميع الأماكن الخطرة.

\n\n

2. الأمان: المترجم صديقك وليس عدوك

\n\n

خطأ مبتدئ نموذجي

\n\n

لنفترض أنك تريد تحويل مؤشر لفئة أساسية إلى مؤشر لفئة مشتقة. التحويل على غرار C سيفعل ذلك بصمت، حتى لو لم تكن الفئات مرتبطة:

\n\n
class 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 في هذه الحالة سيصدر خطأ في الترجمة:

\n\n
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 — لكن هذه قصة أخرى.

\n\n

3. التحكم فيما تفعله

\n\n

ثلاثة تحويلات مختلفة في قوس واحد

\n\n

التحويل على غرار C هو سكين سويسري يمكنه فعل ثلاثة أشياء مختلفة:

\n
    \n
  • static_cast (تحويل عادي بين الأنواع الرقمية أو لأعلى/لأسفل التسلسل الهرمي)
  • \n
  • const_cast (إزالة الثبات)
  • \n
  • reinterpret_cast (إعادة تفسير التمثيل الثنائي)
  • \n
\n\n

هذا مثال يصدم الكثيرين:

\n\n
const                

المدونات

توصيات الكتب