العادية المدلى بها مقابلstatic_cast مقابلdynamic_cast [مكررة]

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

  •  09-06-2019
  •  | 
  •  

سؤال

لقد تم كتابة C و C++ رمز لما يقرب من عشرين عاما, ولكن هناك جانب واحد من هذه اللغات التي لم أفهم أبدا.لقد الواضح العادية يلقي أي

MyClass *m = (MyClass *)ptr;

في كل مكان, ولكن يبدو أن هناك نوعين آخرين من يلقي وأنا لا أعرف الفرق.ما الفرق بين الأسطر التالية من التعليمات البرمجية ؟

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
هل كانت مفيدة؟

المحلول

static_cast

static_cast يستخدم في الحالات حيث تريد أساسا عكس عملية التحويل الضمني ، مع بعض القيود والإضافات. static_cast ينفذ أي وقت الشيكات.هذا ينبغي أن تستخدم إذا كنت تعرف أن كنت تشير إلى كائن من نوع معين ، وبالتالي تحقق سيكون لا لزوم لها.على سبيل المثال:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

في هذا المثال, كنت أعرف أنك مرت MyClass كائن ، وبالتالي ليس هناك أي حاجة إلى وقت تحقق للتأكد من هذا.

dynamic_cast

dynamic_cast مفيد عندما كنت لا تعرف ما الديناميكي نوع من الكائن.تقوم بإرجاع مؤشر فارغة إذا كان الكائن المشار إليها لا تحتوي على نوع مسبوك إلى قاعدة الطبقة (عندما يلقي إلى مرجع ، bad_cast طرح استثناء في هذه الحالة).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

لا يمكنك استخدام dynamic_cast إذا كنت مسبل (يلقي إلى فئة مشتقة) و نوع الوسيطة ليست متعددة الأشكال.على سبيل المثال التعليمات البرمجية التالية ليست صحيحة ، لأن Base لا يحتوي على أي وظيفة افتراضية:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

"حتى الزهر" (الزهر على قاعدة الطبقة) هو دائما صالحة مع كل static_cast و dynamic_cast, و أيضا من دون أي المصبوب ، "حتى يلقي" هو التحويل الضمني.

العادية المدلى بها

هذه القوالب تسمى أيضا ج-أسلوب المدلى بها.ج-أسلوب يلقي متطابقة أساسا إلى محاولة مجموعة من تسلسل C++ يلقي ، وأخذ أولا C++ يلقي يعمل دون النظر في أي وقت مضى dynamic_cast.وغني عن القول أن هذا هو أقوى بكثير كما أنه يجمع بين كل من const_cast, static_cast و reinterpret_cast, لكنه أيضا غير آمنة لأنها لا تستخدم dynamic_cast.

وبالإضافة إلى ذلك, ج-أسلوب يلقي ليس فقط تسمح لك أن تفعل هذا ، ولكن أيضا يسمح لك بأمان بها قاعدة خاصة الدرجة ، في حين أن "أي ما يعادل" static_cast تسلسل سوف تعطيك وقت التحويل البرمجي خطأ ذلك.

بعض الناس يفضلون ج-أسلوب يلقي لأن من الإيجاز.أنا استخدامها لمدة الرقمية يلقي فقط استخدام C++ عندما يلقي الأنواع المعرفة من قبل المستخدم المعنية ، كما أنها توفر أكثر صرامة التحقق.

نصائح أخرى

ثابت الزهر

ثابت يلقي ينفذ تحويلات بين متوافق أنواع.وهو مشابه ج-أسلوب المدلى بها ، ولكن هو أكثر تقييدا.على سبيل المثال ، ج-يلقي نمط من شأنها أن تسمح مؤشر صحيح أن نشير إلى شار.

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

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

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

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

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

تفسير الزهر

لإجبار مؤشر التحويل في نفس الطريقة كما في ج-أسلوب يلقي في الخلفية ، تفسير يلقي سوف تستخدم بدلا من ذلك.

int *r = reinterpret_cast<int*>(&c); // forced conversion

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

ديناميكية الزهر

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

ديناميكية يلقي أمثلة

في المثال أدناه ، MyChild مؤشر يتم تحويلها إلى MyBase المؤشر باستخدام ديناميكية المدلى بها.هذا المشتقة-إلى-قاعدة التحويل ينجح, لأن الطفل كائن يشمل كامل قاعدة الكائن.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

المثال التالي محاولات لتحويل MyBase مؤشر إلى MyChild المؤشر.لأن قاعدة كائن لا تحتوي على أكمل وجوه الأطفال هذا المؤشر التحويل سوف تفشل.للإشارة إلى هذا ، فإن ديناميكية يلقي بإرجاع مؤشر فارغة.وهذا يعطي وسيلة مريحة للتحقق من ما إذا كان أو لم يكن التحويل نجحت خلال وقت التشغيل.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

إذا كان مرجع تحويلها بدلا من مؤشر ديناميكية الزهر ثم سوف تفشل قبل رمي bad_cast استثناء.هذا يحتاج إلى أن يتم التعامل معها باستخدام المحاولة-catch.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

ديناميكي أو ثابت الزهر

الاستفادة من استخدام ديناميكية يلقي أنها تسمح للمبرمج أن تحقق ما إذا كان أو لم يكن التحويل نجحت خلال وقت التشغيل.العيب هو أن هناك أداء النفقات العامة المرتبطة القيام بهذا الاختيار.لهذا السبب باستخدام ثابت الزهر كان من الأفضل في المثال الأول ، لأن مشتقة-إلى-قاعدة التحويل سوف تفشل أبدا.

MyBase *base = static_cast<MyBase*>(child); // ok

ومع ذلك ، في المثال الثاني التحويل إما أن تنجح أو تفشل.فإنه سوف تفشل إذا MyBase كائن يحتوي على MyBase سبيل المثال وسوف تنجح إذا كان يحتوي على MyChild سبيل المثال.في بعض الحالات قد لا تكون معروفة حتى وقت التشغيل.عند هذه هي الحالة الديناميكية يلقي خيار أفضل من ساكنة المدلى بها.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

إذا القاعدة-إلى المستمدة التحويل قد تم تنفيذها باستخدام ثابت الزهر بدلا من ديناميكية يلقي التحويل لن يكون فشل.فإنه قد عاد المؤشر الذي يشير إلى غير مكتملة الكائن.Dereferencing مثل هذا المؤشر يمكن أن يؤدي إلى أخطاء وقت التشغيل.

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Const الزهر

هذا واحد يستخدم في المقام الأول إلى إضافة أو إزالة const معدل متغير.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

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

*nonConst = 10; // potential run-time error

Const الزهر بدلا من ذلك تستخدم أساسا عندما يكون هناك وظيفة أن يأخذ غير ثابت مؤشر الحجة ، على الرغم من أنه لا تعديل pointee.

void print(int *p) 
{
   std::cout << *p;
}

وظيفة ثم يمكن أن تنتقل ثابت متغير باستخدام const المدلى بها.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

المصدر والمزيد من التوضيحات

يجب أن ننظر في المادة البرمجة C++/نوع الصب.

أنه يحتوي على وصف جيد من جميع مختلف أنواع الزهر.التالية مأخوذة من الرابط أعلاه:

const_cast

const_cast(التعبير) في const_cast<>() يستخدم لإضافة/إزالة const(نيس) (أو متقلبة نيس) من متغير.

static_cast

static_cast(التعبير) في static_cast<>() يستخدم يلقي بين صحيح الأنواع.'على سبيل المثال' شار->طويلة ، الباحث->قصيرة.... الخ

ثابت الزهر يستخدم أيضا أن يلقي المؤشرات ذات الصلة أنواع ، مثال صب الفراغ* إلى النوع المناسب.

dynamic_cast

ديناميكية الزهر يستخدم لتحويل المؤشرات والمراجع في وقت التشغيل ، عموما لغرض الصب مؤشر أو مرجع أعلى أو لأسفل الميراث سلسلة (ميراث الهرمي).

dynamic_cast(التعبير)

نوع الهدف يجب أن يكون هناك مؤشر أو نوع مرجع ، التعبير يجب تقييم إلى مؤشر أو إشارة.ديناميكية يلقي يعمل فقط عندما يكون نوع الكائن الذي تعبير يشير هو متوافق مع نوع الهدف وقاعدة فصل واحد على الأقل الأعضاء الظاهرية وظيفة.إذا لم يكن كذلك ، ونوع من التعبير أن يلقي هو مؤشر باطل هو عاد إذا ديناميكية يلقي على إشارة يفشل ، bad_cast طرح استثناء.عندما لا تفشل ، الديناميكي يلقي عودة المؤشر أو إشارة من نوع الهدف إلى الكائن إلى التعبير التي أشارت إليها.

reinterpret_cast

تفسير يلقي ببساطة يلقي نوع واحد أحادي المعامل إلى آخر.أي مؤشر أو جزءا لا يتجزأ من النوع يمكن أن يكون مسبوك أي مع تفسير المدلى بها ، تسمح بسهولة عن سوء الاستخدام.فعلى سبيل المثال, مع تفسير يلقي واحد قد امن يلقي مؤشر صحيح على سلسلة مؤشر.

لمعلوماتك, وأعتقد Bjarne Stroustrup هو قوله ج-أسلوب يلقي هي التي ينبغي تجنبها و أنه يجب استخدام static_cast أو dynamic_cast إذا كان ذلك ممكنا.

Barne Stroustrup C++ نمط أسئلة وأجوبة

تأخذ هذه النصيحة على ما سوف.أنا أبعد ما يكون عن كونه C++ المعلم.

تجنب استخدام ج-أسلوب يلقي.

ج-أسلوب يلقي هي مزيج من const تفسير الزهر, و من الصعب أن تجد-و-استبدال في التعليمات البرمجية الخاصة بك.تطبيق C++ مبرمج يجب تجنب ج-أسلوب المدلى بها.

ج-أسلوب يلقي الخلط const_cast, static_cast ، reinterpret_cast.

أتمنى C++ لم يكن لديك ج-أسلوب يلقي.C++ يلقي تبرز بشكل صحيح (كما يجب ؛ يلقي عادة مؤشرا على فعل شيء سيء) بشكل صحيح التمييز بين أنواع مختلفة من التحويل أن يلقي أداء.كما أنها تسمح مماثلة تبحث الوظائف أن تكون مكتوبة ، على سبيل المثالدفعة::lexical_cast التي هي لطيفة جدا من الاتساق المنظور.

dynamic_cast وقد التشغيل التحقق من نوع يعمل فقط مع المراجع مؤشرات ، في حين static_cast لا تقدم نوع وقت التشغيل التحقق.للحصول على معلومات كاملة ، انظر المقالة MSDN static_cast المشغل.

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

ج-النمط (وغيرها) يلقي تم تغطيتها في إجابات أخرى.

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