الفئة الموروثة "خطأ مؤشر غير صالح" عند استدعاء الوظائف الافتراضية

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

سؤال

كما ترون في الكود أدناه، لدي فئة قاعدة مجردة "HostWindow"، والطبقة التي تستمد منها "Chrome". يتم تنفيذ جميع الوظائف في Chrome. القضية هي أنه لا يمكنني الاتصال بوظائف في Chrome إذا كانت افتراضية.

class HostWindow : public Noncopyable {
public:
    virtual ~HostWindow() { }

    // Pure virtual functions:
    virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false) = 0;
    virtual void scrollbarsModeDidChange() const = 0;
}

class Chrome : public HostWindow {
    // HostWindow functions:
    virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false);
    virtual void scrollbarsModeDidChange() const;

    void focus() const;
}

لذلك دعونا نقول أن لدينا مثيل من الكروم، ونحن نسمي بعض الوظائف:

WebCore::Chrome *chrome = new Chrome();
chrome->repaint(IntRect(), true); // Null pointer error
chrome->focus(); // returns void (works)

خطأ مؤشر NULL أحصل عليه كلما استدعاء الوظائف الافتراضية هو:

برنامج تلقى إشارة exc_bad_access، لا يمكن الوصول إلى الذاكرة. السبب: kern_protection_failure في العنوان: 0x00000008

أي فكرة ما يحدث؟

تحديث:كما أشار الكثير منكم - هذا الرمز يعمل بالفعل. لسوء الحظ، لا يمكنني تقديم مثال أكثر اكتمالا، لأن الكود عميق داخل Webcore (WebKit). ومع ذلك، فقد ضاقت المشكلة لأسفل. إذا قمت بإنشاء مثيل كروم يدويا، استدعاء الوظائف الافتراضية تعمل. وبالتالي فإن المشكلة هي مع هذا مثيل الكروم المعين - لا يمكن إرسافه بشكل صحيح. الآن، تم إنشاء مثيل Chrome في منشئ فئة أخرى. سوف تحقق أكثر ...

تحديث 2:حسنا، يدرس فحص Vtable على المثيل المخالف أنه لاغيا؛ من GDB:

p *(void **)chrome
$52 = (void *) 0x0

مثيل عادي لديه Vtable الصحيح. لذلك، يجب أن أعمل لماذا VTable Nil - أتساءل كيف يمكن أن يحدث ذلك؟ ربما لأنه يجري إنشاء مثيل في بعض الفصول الأخرى منشئ؟

تحديث 3:يبدو أنني صحيحة حول المشكلة في كونها مثيل لها داخل منشئ فئة أخرى.

لذلك، قبل أن تبدو مثيلها مثل هذا:

Page::Page(ChromeClient* chromeClient, ...)
    : m_chrome(new Chrome(this, chromeClient))

و m_chrome هو مثيل غير صالح، مع vtable nil. لقد قمت بتغيير النسبة، لذلك يحدث ذلك عندما تكون هناك حاجة إلى المتغير في المرة الأولى (يتضمن هذا إنقاذ chromeclient لاحقا):

Page::Page(ChromeClient* chromeClient, ...)
    : m_chrome(0)
    , m_chrome_client(chromeClient)

Chrome* Page::chrome() const {
  if(!m_chrome) {
    m_chrome = new Chrome(this, m_chrome_client);
  }
  return m_chrome;
}

الآن الصفحة :: مثيل Chrome () هو الصحيح، مع VTable المناسبة - غريب نوعا ما!

تحديث 4:آخر تحديث، أعدك :). حسنا، لذلك حددت ذلك بالضبط. يمكنك الحصول على المثيل الصحيح، مع VTable، إذا قمت بإنشاء مثيل لها في جسم منشئ الصفحات. إذا قمت بإنشاء مثيل لها في رأس منشئ الصفحات، فلن يكون له VTable. هل هناك أي قيود في أنواع الإعداد المتغير يمكنك القيام به في رأس المنشئ؟ أعتقد أن هذا سؤال آخر Stackoverflow.

شكرا يا شباب لكونهم مفيدون للغاية.

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

المحلول

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

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

أفضل نصيحة يمكنني أن أقدمها لك هي إنشاء أنبوب اختبار أصغر بكثير. إما ستجد مشكلتك أو ستنتهي بك الأمر بمثال آخر.

لا يتم وضع VTBL في مثيل حتى نهاية عملية البناء. في الواقع، تتطلب المواصفات تعديل تدريجي من VTBL لتتناسب مع حالة بناء التسلسل الهرمي للفئة.

نصائح أخرى

يمكنك نشر الكود الكامل؟

بعد تعديل طفيف في التعليمات البرمجية الخاصة بك (كل ما هو متاح)، يعمل:

#include <iostream>

class HostWindow  {
public:
    virtual ~HostWindow() { }

    // Pure virtual functions:
    virtual void repaint(const int , bool contentChanged, bool immediate = false, bool repaintContentOnly = false) = 0;
    virtual void scrollbarsModeDidChange() const = 0;
};

class Chrome : public HostWindow {
public:
    // HostWindow functions:
    virtual void repaint(const int , bool contentChanged, bool immediate = false, bool repaintContentOnly = false) 
    {
        std::cout << "In repaint." << std::endl;
    }
    virtual void scrollbarsModeDidChange() const { }

    void focus() const
    {
        std::cout << "In focus." << std::endl;
    }
};

int main()
{
    Chrome *chrome = new Chrome();
    chrome->repaint(1, true); // Null pointer error
    chrome->focus();
    delete chrome;
    return 0;
}

أنا لست على دراية قاعدة التعليمات البرمجية لديك، ولكن يجب ألا تكتب ما يلي:

// note the 'WebCore::Chrome()'
WebCore::Chrome *chrome = new WebCore::Chrome();
chrome->repaint(IntRect(), true); // 'chrome' should be a valid pointer now

بدلاً من:

WebCore::Chrome *chrome = new Chrome();
chrome->repaint(IntRect(), true); // Null pointer error

SSUME الخاص بك غير قابلة للتصفية هي كما يلي (على الأقل لملي)

class NonCopyable
{
protected:
    NonCopyable() {}
    ~NonCopyable() {}
private:
    NonCopyable( const NonCopyable& );
    const NonCopyable& operator=( const NonCopyable& );
};

بعد إدراج عام معدل لوظيفة الطبقة الكروم وبعض التنفيذ الداومي بالنسبة لهم، كل شيء يعمل دون مشكلة مذكرة.

لا توجد مشكلة في نشر الرمز، فقد تكون قد تفعل أشياء خاطئة وعدم نشر هذه الجزء هنا.

أخيرا، تحقق التحقق من فشل التخصيص. (نعم، "جديد" تخصيصها على كومة)

لقد وجدت أن هذا كان ناجما عن السماح بتصدير جميع الرموز.

عادة، يحتوي Webcore فقط على مجموعة فرعية من الرموز المصدرة - أساسا على الأشياء التي يحتاجها WebKit.

لقد غيرت ذلك لتصدير كل رمز - وتسبب بطريقة أو بأخرى في هذا الخطأ.

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