لماذا تستخدم static_cast<int>(x) بدلاً من (int)x؟

StackOverflow https://stackoverflow.com/questions/103512

  •  01-07-2019
  •  | 
  •  

سؤال

لقد سمعت أن static_cast يجب تفضيل الوظيفة على نمط C أو أسلوب الوظيفة البسيط.هل هذا صحيح؟لماذا؟

هل كانت مفيدة؟

المحلول

السبب الرئيسي هو أن قوالب C الكلاسيكية لا تميز بين ما نسميه static_cast<>(), reinterpret_cast<>(), const_cast<>(), ، و dynamic_cast<>().هذه الأشياء الأربعة مختلفة تماما.

أ static_cast<>() عادة ما تكون آمنة.يوجد تحويل صالح في اللغة، أو مُنشئ مناسب يجعل ذلك ممكنًا.المرة الوحيدة التي يكون فيها الأمر محفوفًا بالمخاطر قليلاً هي عندما تنتقل إلى فئة موروثة؛يجب عليك التأكد من أن الكائن هو في الواقع السليل الذي تدعي أنه هو، بوسائل خارجية عن اللغة (مثل العلم في الكائن).أ dynamic_cast<>() آمن طالما تم التحقق من النتيجة (المؤشر) أو تم أخذ الاستثناء المحتمل في الاعتبار (المرجع).

أ reinterpret_cast<>() (أو أ const_cast<>()) من ناحية أخرى هو دائما خطير.أنت تقول للمترجم:"ثق بي:أعلم أن هذا لا يبدو مثل foo (يبدو هذا كما لو أنه غير قابل للتغيير)، لكنه كذلك".

المشكلة الأولى هي أنه يكاد يكون من المستحيل معرفة أي منها سيحدث في قالب من النمط C دون النظر إلى أجزاء كبيرة ومتفرقة من التعليمات البرمجية ومعرفة جميع القواعد.

لنفترض هذه:

class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;

CMyBase  *pSomething; // filled somewhere

والآن يتم تجميع هذين الاثنين بنفس الطريقة:

CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked

pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
                                     // Safe; as long as we checked
                                     // but harder to read

ومع ذلك، دعونا نرى هذا الرمز المتطابق تقريبًا:

CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert

pOther = (CMyOtherStuff*)(pSomething);            // No compiler error.
                                                  // Same as reinterpret_cast<>
                                                  // and it's wrong!!!

كما ترون، لا توجد طريقة سهلة للتمييز بين الحالتين دون معرفة الكثير عن جميع الفئات المعنية.

المشكلة الثانية هي أنه من الصعب جدًا تحديد موقع القوالب ذات النمط C.في التعبيرات المعقدة، قد يكون من الصعب جدًا رؤية القوالب ذات النمط C.يكاد يكون من المستحيل كتابة أداة تلقائية تحتاج إلى تحديد موقع القوالب ذات النمط C (على سبيل المثال أداة بحث) بدون واجهة أمامية كاملة لبرنامج التحويل البرمجي C++.ومن ناحية أخرى، من السهل البحث عن "static_cast<" أو "reinterpret_cast<".

pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
      // No compiler error.
      // but the presence of a reinterpret_cast<> is 
      // like a Siren with Red Flashing Lights in your code.
      // The mere typing of it should cause you to feel VERY uncomfortable.

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

نصائح أخرى

نصيحة عملية واحدة:يمكنك البحث بسهولة عن الكلمة الأساسية static_cast في كود المصدر الخاص بك إذا كنت تخطط لترتيب المشروع.

باختصار:

  1. static_cast<>() يمنحك القدرة على التحقق من وقت الترجمة ، لا يفعل ذلك.
  2. static_cast<>() يمكن رصدها بسهولة في أي مكان داخل رمز مصدر C ++ ؛في المقابل، من الصعب اكتشاف فريق C_Style.
  3. يتم نقل النوايا بشكل أفضل باستخدام قوالب C++.

مزيد من التوضيح:

ينفذ فريق التمثيل الثابت التحويلات بين أنواع متوافقة.إنه يشبه فريق C-Style ، ولكنه أكثر تقييدًا.على سبيل المثال ، سيسمح فريق C-Style Cast بمؤشر عدد صحيح للإشارة إلى Char.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

نظرًا لأن هذا ينتج عن مؤشر 4 بايت يشير إلى 1 بايت من الذاكرة المخصصة ، فإن الكتابة إلى هذا المؤشر إما أن تسبب خطأ في وقت التشغيل أو سوف يكتب بعض الذاكرة المجاورة.

*p = 5; // run-time error: stack corruption

على عكس فريق C-Style Cast ، سيسمح الممثلون الثابتون للمترجم بالتحقق من أن أنواع بيانات المؤشر والنقطة متوافقة ، مما يسمح للمبرمج بالتقاط هذه المهمة المؤشر غير الصحيحة أثناء التجميع.

int *q = static_cast<int*>(&c); // compile-time error

اقرأ المزيد عن:
ما هو الفرق بين static_cast<> وصب النمط C
و
طاقم الممثلين العاديين مقابل.static_cast مقابل.Dynamic_cast

السؤال أكبر من مجرد استخدام أسلوب static_cast أو C لأن هناك أشياء مختلفة تحدث عند استخدام قوالب نمط C.تهدف عوامل تشغيل C++ إلى جعل هذه العمليات أكثر وضوحًا.

على السطح، تظهر قوالب static_cast ونمط C لنفس الشيء، على سبيل المثال عند تحويل قيمة إلى أخرى:

int i;
double d = (double)i;                  //C-style cast
double d2 = static_cast<double>( i );  //C++ cast

كلاهما يحول القيمة الصحيحة إلى مضاعفة.ولكن عند العمل مع المؤشرات تصبح الأمور أكثر تعقيدًا.بعض الأمثلة:

class A {};
class B : public A {};

A* a = new B;
B* b = (B*)a;                                  //(1) what is this supposed to do?

char* c = (char*)new int( 5 );                 //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error

في هذا المثال (1) ربما يكون الأمر جيدًا لأن الكائن المشار إليه بواسطة A هو في الحقيقة مثيل لـ B.ولكن ماذا لو كنت لا تعرف في هذه المرحلة من الكود ما الذي يشير إليه فعليًا؟(2) ربما يكون قانونيًا تمامًا (تريد فقط النظر إلى بايت واحد من العدد الصحيح)، ولكن قد يكون خطأ أيضًا وفي هذه الحالة سيكون الخطأ لطيفًا، مثل (3).تهدف عوامل تشغيل C++‎ إلى كشف هذه المشكلات في التعليمات البرمجية من خلال توفير أخطاء وقت الترجمة أو وقت التشغيل عندما يكون ذلك ممكنًا.

لذلك، من أجل "صب القيمة" الصارم، يمكنك استخدام static_cast.إذا كنت تريد صب المؤشرات متعددة الأشكال في وقت التشغيل، فاستخدمdynamic_cast.إذا كنت تريد حقا أن تنسى الأنواع، فيمكنك استخدام Reintrepret_cast.ولرمي const خارج النافذة، يوجد const_cast.

إنهم فقط يجعلون الكود أكثر وضوحًا بحيث يبدو وكأنك تعرف ما كنت تفعله.

static_cast يعني أنه لا يمكنك عن طريق الخطأ const_cast أو reinterpret_cast, وهو أمر جيد.

  1. يتيح العثور على الممثلين بسهولة في الكود الخاص بك باستخدام GREP أو أدوات مماثلة.
  2. يجعل من الصريح نوع الممثل الذي تقوم به ، وإشراك مساعدة المترجم في فرضه.إذا كنت ترغب فقط في التخلص من الوفرة ، فيمكنك استخدام const_cast ، والتي لن تسمح لك بالقيام بأنواع أخرى من التحويلات.
  3. الممثلات قبيحة بطبيعتها - أنت كمبرمج تقوم بإلغاء كيفية تعامل المترجم عادةً إلى رمزك.أنت تقول للمترجم ، "أعرف أفضل منك". ولما كان الأمر كذلك ، فمن المنطقي أن يكون أداء فريق الممثلين أمرًا مؤلمًا بشكل معتدل ، وأنه يجب عليهم التمسك في الكود الخاص بك ، لأنهم مصدر محتمل للمشاكل.

يرى فعالة C ++ مقدمة

يتعلق الأمر بمدى أمان النوع الذي تريد فرضه.

عندما تكتب (bar) foo (وهو ما يعادل reinterpret_cast<bar> foo إذا لم تقم بتوفير عامل تحويل النوع) فأنت تطلب من المترجم تجاهل أمان النوع، ويفعل ما يُطلب منه فقط.

عندما تكتب static_cast<bar> foo أنت تطلب من المترجم أن يتأكد على الأقل من أن تحويل النوع منطقي، وبالنسبة للأنواع المتكاملة، أن يقوم بإدراج بعض أكواد التحويل.


تحرير 2014-02-26

لقد كتبت هذه الإجابة منذ أكثر من 5 سنوات، وأخطأت فيها.(انظر التعليقات.) لكنها لا تزال تحصل على أصوات مؤيدة!

static_cast، بصرف النظر عن معالجة المؤشرات إلى الفئات، يمكن استخدامه أيضًا لإجراء تحويلات محددة بشكل صريح في الفئات، وكذلك لإجراء تحويلات قياسية بين الأنواع الأساسية:

double d = 3.14159265;
int    i = static_cast<int>(d);

من السهل تفويت قوالب C Style في مجموعة من التعليمات البرمجية.إن قوالب أسلوب C++ ليست فقط ممارسة أفضل؛أنها توفر درجة أكبر بكثير من المرونة.

يسمح reinterpret_cast بتحويلات متكاملة لنوع المؤشر، ولكن يمكن أن يكون غير آمن في حالة إساءة استخدامه.

يقدم static_cast تحويلاً جيدًا للأنواع الرقمية، على سبيل المثال.من التعدادات إلى ints أو ints إلى floats أو أي أنواع بيانات تثق في كتابتها.لا يقوم بإجراء أي فحوصات وقت التشغيل.

من ناحية أخرى، سيقوم Dynamic_cast بإجراء عمليات التحقق هذه للإشارة إلى أي تخصيصات أو تحويلات غامضة.إنه يعمل فقط على المؤشرات والمراجع ويتحمل نفقات عامة.

هناك بضعة آخرين ولكن هذه هي أهم الأشياء التي ستصادفها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top