هل يؤثر استخدام __declspec(novtable) في الفئات الأساسية المجردة على RTTI بأي شكل من الأشكال؟

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

سؤال

أم أن هناك أي آثار سلبية أخرى معروفة لاستخدام __declspec(novtable)؟لا يمكنني العثور على إشارات لأية مشكلات.

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

المحلول

يستخدم MSCV one vptr per object and one vtbl per class لتنفيذ آلية OO مثل RTTI والوظائف الافتراضية.
لذلك ستعمل وظائف RTTI والظاهرية بشكل جيد فقط إذا تم ضبط vptr بشكل صحيح.

struct __declspec(novtable) B {
    virtual void f() = 0;
};
struct D1 : B {
    D1() {
    }       // after the construction of D1, vptr will be set to vtbl of D1.
};
D1 d1;      // after d has been fully constructed, vptr is correct.
B& b = d1;  // so virtual functions and RTTI will work.
b.f();      // calls D1::f();
assert( dynamic_cast<D1*>(&b) );
assert( typeid(b) == typeid(D1) );

يجب أن يكون B فئة مجردة عند الاستخدام __declspec(novtable).
لن يكون هناك مثيل لـ B إلا في مُنشئ D1.
و__declspec(novtable) ليس له أي تأثيرات سلبية في معظم الحالات.

ولكن أثناء بناء الطبقة المشتقة __declspec(novtable) سيجعله مختلفًا عن الدلالي ISO C++.

struct D2 : B {


    D2() {  // when enter the constructor of D2 \  
            //     the vtpr must be set to vptr of B \
            //     if  B didn't use __declspec(novtable).
            // virtual functions and RTTI will also work.

            this->f(); // should calls B::f();
            assert( typeid(*this) == typeid(B) );
            assert( !dynamic_cast<D2*>(this) );
            assert( dynamic_cast<B*>(this) );

            // but __declspec(novtable) will stop the compiler \
            //    from generating code to initialize the vptr.
            // so the code above will crash because of uninitialized vptr.
    }
};

ملحوظة:الظاهري و () = 0;يجعل f ليكون أ pure virtual function و B لتكون فئة مجردة.
ال definition من وظيفة افتراضية خالصة could (لا must) تكون مفقودة.
يسمح C++ باستدعاء الوظيفة الافتراضية في المُنشئ وهو ما لا نوصي به.

تحديث:خطأ في D2:vptr في المنشئ المشتق.

struct D3 : B {  // ISO C++ semantic
    D3() {       // vptr must be set to vtbl of B before enter
    }            // vptr must be set to vtbl of D2 after leave
};

لكن vptr غير محدد أثناء الإنشاء. وهذا أحد أسباب عدم التوصية باستدعاء الوظيفة الافتراضية في المُنشئ.

إذا كان vptr في D2::D2() هو B وكان تعريف B::f() مفقودًا، this->f(); سوف يتعطل عند إلغاء الإشارة من المؤشر إلى الوظيفة في ملف vtbl.
إذا كان vptr في D2::D2() B وB فاستخدم novtable، this->f(); سوف يتعطل عند إلغاء الإشارة إلى vptr غير مهيأ.

في الواقع، vptr في D2::D2() هو D2 في MSVC(msvc8). قام المترجم بتعيين vptr على D2 قبل تنفيذ تعليمات برمجية أخرى في D2::D2().
لذا this->f(); يستدعي D2::f() وسيتم انتهاك التأكيدات الثلاثة.

نصائح أخرى

وإذا فهمت بشكل صحيح: يتم تحويل أي دعوة FN افتراضية داخل المنشئ أو dtor إلى ربط وقت التحويل البرمجي. ونحن لا يمكن إجراء مكالمات FN الظاهري من (ج / د) الاختصاصات. والسبب هو أنه في الوقت الذي يتم الحصول على إنشاء كائن من الفئة الأساسية ليس لديها أي معرفة فئة مشتقة وبالتالي لا يمكن أن تجعل من الدعوة إلى فئة مشتقة وw.r.t وdtors ينطبق نفس المنطق.

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