لماذا تتصرف المهمة الافتراضية بشكل مختلف عن الوظائف الافتراضية الأخرى لنفس التوقيع؟

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

سؤال

أثناء اللعب مع تنفيذ مشغل التعيين الظاهري، انتهيت بسلوك مضحك. إنها ليست خلل مترجم، نظرا لأن G ++ 4.1 و 4.3 و VS 2005 يشاركنا نفس السلوك.

في الأساس، يتصرف المشغل الظاهري = بشكل مختلف عن أي وظيفة افتراضية أخرى فيما يتعلق بالتعليمة التي يتم تنفيذها بالفعل.

struct Base {
   virtual Base& f( Base const & ) {
      std::cout << "Base::f(Base const &)" << std::endl;
      return *this;
   }
   virtual Base& operator=( Base const & ) {
      std::cout << "Base::operator=(Base const &)" << std::endl;
      return *this;
   }
};
struct Derived : public Base {
   virtual Base& f( Base const & ) {
      std::cout << "Derived::f(Base const &)" << std::endl;
      return *this;
   }
   virtual Base& operator=( Base const & ) {
      std::cout << "Derived::operator=( Base const & )" << std::endl;
      return *this;
   }
};
int main() {
   Derived a, b;

   a.f( b ); // [0] outputs: Derived::f(Base const &) (expected result)
   a = b;    // [1] outputs: Base::operator=(Base const &)

   Base & ba = a;
   Base & bb = b;
   ba = bb;  // [2] outputs: Derived::operator=(Base const &)

   Derived & da = a;
   Derived & db = b;
   da = db;  // [3] outputs: Base::operator=(Base const &)

   ba = da;  // [4] outputs: Derived::operator=(Base const &)
   da = ba;  // [5] outputs: Derived::operator=(Base const &)
}

التأثير هو أن المشغل الظاهري = لديه سلوك مختلف عن أي وظيفة افتراضية أخرى بنفس التوقيع ([0] مقارنة [1])، عن طريق استدعاء الإصدار الأساسي من المشغل عند الاتصال به من خلال كائنات مشتقة حقيقية ([1] ) أو المراجع المشتقة ([3]) أثناء تنفيذها كدالة افتراضية منتظمة عند استدعاء المراجع الأساسية ([2])، أو عندما تكون LVALUE أو RVALUE المراجع الأساسية والمرجع المشتق ([4]، [5]).

هل هناك أي شرح معقول لهذا السلوك الغريب؟

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

المحلول

إليك كيف تسير الأمور:

إذا قمت بتغيير [1] إلى

a = *((Base*)&b);

ثم تعمل الأمور بالطريقة التي تتوقعها. هناك مشغل تعيين تم إنشاؤه تلقائيا في Derived يبدو أن هذا:

Derived& operator=(Derived const & that) {
    Base::operator=(that);
    // rewrite all Derived members by using their assignment operator, for example
    foo = that.foo;
    bar = that.bar;
    return *this;
}

في مثال التحويل البرمجيات لديك معلومات كافية لتخمين ذلك a و b هي من النوع Derived وهكذا يختارون استخدام المشغل الذي تم إنشاؤه تلقائيا أعلاه التي تستدعي لك. هذه هي الطريقة التي حصلت عليها [1]. مواتية قوى مؤشر بلدي للقيام بذلك في طريقك، لأنني أقول مترجم إلى "أنسى" ذلك b هو من النوع Derived وهكذا يستخدم Base.

يمكن تفسير النتائج الأخرى بنفس الطريقة.

نصائح أخرى

هناك ثلاثة مشغل = في هذه الحالة:

Base::operator=(Base const&) // virtual
Derived::operator=(Base const&) // virtual
Derived::operator=(Derived const&) // Compiler generated, calls Base::operator=(Base const&) directly

هذا ما يفسر السبب في أنه يبدو وكأنه قاعدة :: المشغل = (قاعدة const &) يسمى "تقريبا" في حالة [1]. يطلق عليه من النسخة التي تم إنشاؤها بالمترجم. الأمر نفسه ينطبق على القضية [3]. في حالة 2، فإن الوسيطة الجانبية اليمنى "BB" لديها قاعدة نوع &،، لذلك لا يمكن استدعاء المشغل :: المشغل = (المشتق &).

لا يوجد مشغل تعيين مستخدم يتم تقديمه مقابل فئة مشتقة. وبالتالي، يتم توليف المحول البرمجي بمثابة مشغل تعيين الفصل الدراسي قاعدة داخليا من مشغل التعيين المريئي للفئة المشتقة.

virtual Base& operator=( Base const & ) //is not assignment operator for Derived

لذلك، a = b; // [1] outputs: Base::operator=(Base const &)

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

ba = bb;  // [2] outputs: Derived::operator=(Base const &)

==> داخليا ==> (Object-> VTable [عامل التعيين]) احصل على إدخال عامل التعيين في VTable من الفصل الذي ينتمي إليه الكائن واستدعاء الطريقة.

إذا فشلت في تقديم مناسب operator= (أي أنواع العائد والوسيطة الصحيحة)، الافتراضي operator= يتم توفير برنامج التحويل البرمجي الذي يزيد عن تحميل أي واحد يعرف المستخدم. في حالتك سوف تسمي Base::operator= (Base const& ) قبل نسخ الأعضاء المشتقين.

افحص هذا حلقة الوصل للحصول على تفاصيل حول المشغل = يجري جعل الظاهري.

السبب في أن هناك مترجم قدمت مهمة افتراضية operator=. وبعد الذي يسمى في السيناريو a = b وكما نعرف افتراضيا مشغل تعيين قاعدة الاستدعاء داخليا.

يمكن العثور على المزيد من التفسير حول المهمة الافتراضية في: https://stackoverflow.com/a/26906275/3235055.

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