سؤال

أحيانا تلاحظ البرامج التي تحطم على جهاز الكمبيوتر مع الخطأ:"نقية الظاهري استدعاء دالة".

كيف هذه البرامج حتى تجميع عندما لا يمكن إنشاء كائن من فئة مجردة?

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

المحلول

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

(انظر عرض حي هنا)

class Base
{
public:
    Base() { doIt(); }  // DON'T DO THIS
    virtual void doIt() = 0;
};

void Base::doIt()
{
    std::cout<<"Is it fine to call pure virtual function from constructor?";
}

class Derived : public Base
{
    void doIt() {}
};

int main(void)
{
    Derived d;  // This will cause "pure virtual function call" error
}

نصائح أخرى

فضلا عن المعيار حالة استدعاء دالة ظاهري من منشئ أو المدمر كائن مع خالص وظائف افتراضية يمكنك أيضا الحصول على دالة ظاهري الاتصال (على MSVC على الأقل) إذا كنت استدعاء دالة ظاهري بعد إتلاف كائن.من الواضح أن هذا هو سيئة جدا الشيء في محاولة تفعل ولكن إذا كنت تعمل مع فئات مجردة كما واجهات و نخفق ثم إنه شيء قد ترى.انها ربما أكثر احتمالا إذا كنت تستخدم المشار إليها تحسب واجهات لديك المرجع الاعتماد علة أو إذا كان لديك كائن استخدام/تدمير الكائن حالة تعارض في برنامج متعدد الخيوط...الشيء حول هذه الأنواع من purecall هو أنه في كثير من الأحيان أقل من السهل فهم ما يجري كما شيكا "المشتبه بهم المعتادين" الظاهري المكالمات في المنشئ و dtor سوف يأتي نظيفة.

للمساعدة في تصحيح هذه الأنواع من المشاكل في الإصدارات المختلفة من MSVC استبدال وقت التشغيل المكتبة purecall معالج.يمكنك القيام بذلك عن طريق توفير وظيفة الخاصة بك مع هذا التوقيع:

int __cdecl _purecall(void)

وربط ذلك قبل أن ربط مكتبة وقت التشغيل.هذا يتيح لك السيطرة على ما يحدث عندما purecall يتم الكشف عن.مرة واحدة لديك التحكم يمكنك أن تفعل شيئا أكثر فائدة من مستوى معالج.لدي معالج التي يمكن أن توفر تتبع المكدس من حيث purecall حدث ؛ انظر هنا: http://www.lenholgate.com/blog/2006/01/purecall.html للحصول على مزيد من التفاصيل.

(ملاحظة يمكنك أيضا استدعاء _set_purecall_handler() لتثبيت معالج في بعض الإصدارات من MSVC).

عادة عند استدعاء دالة ظاهري من خلال التعلق مؤشر--على الأرجح سبيل المثال تم بالفعل تدمير.

يمكن أن يكون هناك أكثر "خلاقة" من الأسباب أيضا:وربما كنت قد تمكنت من قطع جزء من الكائن الخاص بك حيث الظاهري وظيفة تنفيذها.ولكن عادة هو فقط ذلك سبيل المثال تم بالفعل تدمير.

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

محض تكهنات

تحرير: يبدو أني مخطئ في هذه القضية.OTOH IIRC بعض اللغات لا تسمح vtbl ينادي من منشئ المدمر.

يمكنني استخدام VS2010 و كلما حاولت الاتصال المدمر مباشرة من طريقة يمكنني الحصول على "النقي الظاهري استدعاء دالة" خطأ أثناء وقت التشغيل.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void SomeMethod1() { this->~Foo(); }; /* ERROR */
};

لذلك انتقلت إلى ما هو داخل ~فو() فصل خاص الأسلوب ، ثم تعمل مثل السحر.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void _MethodThatDestructs() {};
  void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */
};

إذا كنت تستخدم Borland/CodeGear/Embarcadero/Idera C++ Builder الخاص بك يمكن فقط تنفيذ

extern "C" void _RTLENTRY _pure_error_()
{
    //_ErrorExit("Pure virtual function called");
    throw Exception("Pure virtual function called");
}

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

PS.وظيفة الأصلي هو [C++ Builder]\source\cpprtl\Source\misc\pureerr.cpp

ركضت إلى السيناريو الذي نقية وظائف افتراضية يحصل من تدمير الكائنات ، Len Holgate بالفعل جميل جدا الإجابة أود لإضافة بعض اللون مع مثال:

  1. كائن مشتق إنشاء المؤشر (قاعدة الطبقة) ، حفظها في مكان ما
  2. مشتقة حذف الكائن ، ولكن ما المؤشر لا يزال المشار إليها
  3. المؤشر الذي يشير إلى حذف المشتقة كائن يحصل تسمى

فئة مشتقة المدمر تعيين vptr نقطة إلى قاعدة الطبقة vtable الذي لديه وظيفة افتراضية نقية ، لذلك عندما ندعو الظاهري وظيفة ، وهو في الحقيقة يدعو إلى نقية virutal منها.

هذا يمكن أن يحدث بسبب واضحة رمز علة ، أو معقدة سيناريو حالة السباق في خيوط متعددة البيئات.

هنا هو مثال بسيط (g++ ترجمة مع التحسين إيقاف - ببساطة البرنامج يمكن بسهولة الأمثل بعيدا):

 #include <iostream>
 using namespace std;

 char pool[256];

 struct Base
 {
     virtual void foo() = 0;
     virtual ~Base(){};
 };

 struct Derived: public Base
 {
     virtual void foo() override { cout <<"Derived::foo()" << endl;}
 };

 int main()
 {
     auto* pd = new (pool) Derived();
     Base* pb = pd;
     pd->~Derived();
     pb->foo();
 }

و تتبع المكدس تبدو مثل:

#0  0x00007ffff7499428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff749b02a in __GI_abort () at abort.c:89
#2  0x00007ffff7ad78f7 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7adda46 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7adda81 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7ade84f in __cxa_pure_virtual () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x0000000000400f82 in main () at purev.C:22

تسليط الضوء على:

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

هنا هو متستر طريقة لذلك أن يحدث.كان هذا أساسا يحدث لي اليوم.

class A
{
  A *pThis;
  public:
  A()
   : pThis(this)
  {
  }

  void callFoo()
  {
    pThis->foo(); // call through the pThis ptr which was initialized in the constructor
  }

  virtual void foo() = 0;
};

class B : public A
{
public:
  virtual void foo()
  {
  }
};

B b();
b.callFoo();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top