هل هناك حالات يعلن فيها الفصل الأساليب الافتراضية ولا يحتاج المترجم إلى استخدام VPTR؟

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

  •  22-09-2019
  •  | 
  •  

سؤال

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

على سبيل المثال ، ضع في اعتبارك:

#include <iostream>
struct FooBase
{
  virtual void bar()=0;
};

struct FooDerived : public FooBase
{
  virtual void bar() { std::cout << "FooDerived::bar()\n"; }
};

int main()
{
   FooBase* pFoo = new FooDerived();
   pFoo->bar();

  return 0;
}

في هذا المثال ، يعرف المترجم بالتأكيد ما هو نوع PFOO في وقت الترجمة ، لذلك لا يحتاج إلى استخدام VPTR لـ PFOO ، أليس كذلك؟ هل هناك حالات أكثر إثارة للاهتمام حيث يمكن للمترجم تجنب استخدام VPTR؟

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

المحلول

بناء على إجابة أندرو شتاين, ، لأنني أعتقد أنك تريد أيضًا أن تعرف متى يمكن تجنب ما يسمى "وقت التشغيل النفقات العامة للوظائف الافتراضية". (النفقات العامة موجودة ، لكنها صغير جدا, ونادرا ما تستحق القلق.)

من الصعب حقًا تجنب الفراغ من مؤشر VTABLE ، ولكن يمكن تجاهل المؤشر نفسه ، بما في ذلك في مثالك. لان pfooتهيئة في هذا النطاق ، يعرف المترجم ذلك pFoo->bar يجب أن يعني foodived :: بار, ، ولا يحتاج إلى التحقق من VTABLE. هناك أيضًا العديد من تقنيات التخزين المؤقت لتجنب عمليات البحث المتعددة VTA ، بدءًا من البسيطة إلى المجمع.

نصائح أخرى

حتى في حالة إظهارك ، أشك في أن أي مترجم سوف يفعل ما تقترحه.

أنت تستخدم برنامجًا بسيطًا جدًا في ملف واحد.

تخيل أنك حصلت على Foobase و Fooderived في foo.h و foo.cpp و Main in Main.cpp. عند تجميع foo.cpp ، كيف يكون المترجم أن يعرف أنه في البرنامج بأكمله ، لا توجد حاجة إلى VTBL. لم يشاهد Main.cpp.

لا يمكن اتخاذ القرار النهائي إلا في وقت الرابط ، عندما يكون الوقت متأخرًا جدًا ومعقدًا لتعديل ملف الكائن ، والعثور على جميع المكالمات إلى حجم (Fooderived) ، وما إلى ذلك.

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

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

في مثالك ، قد يكتشف برنامج التحويل البرمجي الذكي أن النوع الديناميكي من *pFoo الكائن هو FooDerived. من شأن ذلك أن يسمح للمترجم بتحسين الرمز: لإنشاء مكالمة مباشرة إلى FooDerived::bar الوظيفة استجابة ل pFoo->bar() التعبير (دون استخدام الجدول الافتراضي). لكن مع ذلك ، لا يزال يتعين على المترجم عادة تهيئة مؤشر الجدول الظاهري بشكل صحيح في كل منهما FooDerived هدف.

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

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

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