الممثلون الثابتون مقابل فريق Dymamic لاجتياز التسلسلات الهرمية للميراث

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

سؤال

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

مثال:

#include <iostream>
#include <typeinfo>

using namespace std;

class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};
class Other {};

int main() {
    Circle c;

    Shape* s = &c; // Upcast: normal and OK

    // More explicit but unnecessary:
    s = static_cast<Shape*>(&c);
    // (Since upcasting is such a safe and common
    // operation, the cast becomes cluttering)

    Circle* cp = 0;
    Square* sp = 0;

    // Static Navigation of class hierarchies
    // requires extra type information:
    if(typeid(s) == typeid(cp)) // C++ RTTI
        cp = static_cast<Circle*>(s);
    if(typeid(s) == typeid(sp))
        sp = static_cast<Square*>(s);
    if(cp != 0)
        cout << "It's a circle!" << endl;
    if(sp != 0)
        cout << "It's a square!" << endl;

    // Static navigation is ONLY an efficiency hack;
    // dynamic_cast is always safer. However:
    // Other* op = static_cast<Other*>(s);
    // Conveniently gives an error message, while
    Other* op2 = (Other*)s;
    // does not
} ///:~

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

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

المحلول

static_cast في حد ذاته لا يحتاج RTTI - typeid لا (كما يفعل dynamic_cast) ، لكن هذه قضية مختلفة تمامًا. معظم الممثلين هي مجرد إخبار المترجم "ثق بي ، أعرف ما أفعله" - dynamic_cast هو الاستثناء ، يطلب من المترجم التحقق في وقت التشغيل وربما يفشل. هذا هو الفرق الكبير في الأداء هناك!

نصائح أخرى

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

class Shape {
public:
    virtual ~Shape() {};
    virtual void announce() = 0;  // And likewise redeclare in Circle and Square.
};

void Circle::announce() {
    cout << "It's a circle!" << endl;
}

void Square::announce() {
    cout << "It's a square!" << endl;
}

// Later...
s->announce();

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

مزيد من المعلومات: static_cast يفعل ليس تتطلب RTTI ، ولكن يمكن استخدامه باستخدامه غير آمن ، مما يؤدي إلى سلوك غير محدد (على سبيل المثال). dynamic_cast آمن ولكنه بطيء ، لأنه يتحقق (وبالتالي يتطلب) معلومات RTTI. الممثلون القديم على الطراز C هو أكثر آمنة من static_cast لأنه سوف يلقي بهدوء عبر أنواع غير ذات صلة تمامًا ، حيث static_cast سوف يعترض مع خطأ في وقت الترجمة.

مع الممثلين الثابتة (والتحقق من النوع) لا يمكنك مسبل إلى نوع وسيط (مستمد من الأب من الأب من الجد ، لا يمكنك الخروج من الجد إلى الأب) الاستخدام محدود قليلاً. static_cast بدون فحص typeid هو التضحية بالصحة من أجل perfomance ، ثم تعرف ما يقولون:

من الذي يضحى بالصحة من أجل الأداء لا يستحق ولا يستحق

ثم بالطبع ، هناك حالات تحتاج فيها إلى حد ما إلى بعض تعليمات وحدة المعالجة المركزية ، ولا يوجد مكان آخر للبحث عن تحسينات وأنت آمن فعليًا على ما تفعله وكنت قد تم قياسه (أليس كذلك؟) أن المكان الوحيد الذي تقوم به الأداء الكسب يستخدم static_cast بدلاً من Dynamic_cast ... ثم تعلم أنه يجب عليك إعادة صياغة التصميم الخاص بك ، أو خوارزمياتك أو الحصول على أجهزة أفضل.

القيود التي تفرضها باستخدام RTTI + Static_cast هي أنك لن تتمكن من تمديد الكود الخاص بك بفئات مشتقة جديدة في وقت لاحق دون إعادة صياغة جميع الأماكن التي استخدمت فيها هذه الخدعة للحصول على عدد قليل من تعليمات وحدة المعالجة المركزية. من المحتمل أن يستغرق إعادة صياغة نفسها وقتًا أطول (وقت الهندسة أغلى) من وقت وحدة المعالجة المركزية التي حصلت عليها. إذا ، على أي حال ، يكون الوقت المكرسة للهبوطات ملحوظة ، فعليك إعادة صياغة التصميم كما يقترح j_random_hacker ، فسيتحسن في التصميم والأداء.

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

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