Унаследованный класс «ошибка недопустимого указателя» при вызове виртуальных функций

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;
}

Допустим, у нас есть экземпляр Chrome, и мы вызываем несколько функций:

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

Ошибка нулевого указателя, которую я получаю всякий раз, когда вызываю виртуальные функции:

Программа получила сигнал EXC_BAD_ACCESS. Не удалось получить доступ к памяти.Причина:KERN_PROTECTION_FAILURE по адресу:0x00000008

Есть идеи, что происходит?

Обновлять:Как многие из вас отметили, этот код действительно работает.К сожалению, я не могу предоставить более полный пример, поскольку код находится глубоко внутри WebCore (WebKit).Однако я сузил проблему.Если я создаю экземпляр Chrome вручную, вызов виртуальных функций работает.Итак, проблема связана с этим конкретным экземпляром Chrome — он не может быть создан должным образом.Теперь экземпляр Chrome создается в конструкторе другого класса.буду исследовать дальше...

Обновление 2:Хорошо, проверка vtable на экземпляре-нарушителе показывает, что она равна нулю;из ГБД:

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

Обычный экземпляр имеет правильную виртуальную таблицу.Итак, мне нужно выяснить, почему vtable равна нулю. Интересно, как такое могло произойти?Может быть, потому, что он создается в конструкторе некоторых других классов?

Обновление 3:Похоже, я прав в том, что проблема заключается в создании экземпляра внутри конструктора другого класса.

Итак, до создания экземпляра это выглядело так:

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

А m_chrome — недопустимый экземпляр с нулевой виртуальной таблицей.Я изменил создание экземпляра, чтобы оно происходило, когда переменная требуется в первый раз (это предполагает сохранение 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;
}

Теперь экземпляр Page::chrome() правильный, с правильной виртуальной таблицей — довольно странно!

Обновление 4:Последнее обновление, обещаю :).Хорошо, я точно это определил.Вы получите правильный экземпляр с виртуальной таблицей, если создадите его экземпляр в теле конструктора страницы.Если вы создадите его экземпляр в голове конструктора страницы, у него не будет виртуальной таблицы.Есть ли какие-либо ограничения на типы настроек переменных, которые вы можете выполнять в голове конструктора?Я думаю, это еще один вопрос Stackoverflow.

Спасибо, ребята, за то, что вы так помогли.

Это было полезно?

Решение

Да, указатель this равен нулю.Добавьте 8, чтобы получить смещение, и это ваша вина.Судя по всему, у вас вообще нет никакого реального объекта.

Я думаю, поскольку вы не опубликовали достаточно кода, чтобы действительно разобраться.Либо весь указатель this равен 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

предположим, что ваши не подлежащие копированию следующие (по крайней мере, для меня)

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

после вставки общественный модификатор функции класса chrome и некоторая фиктивная реализация для них, все это работало без заявленной проблемы.

с опубликованным кодом проблем нет, возможно, вы делаете что-то неправильно и не публикуете эту часть здесь.

наконец, НЕОБХОДИМО проверить наличие сбоя выделения.(да, «новые» размещаются в куче)

Я обнаружил, что это было вызвано разрешением экспорта всех символов.

Обычно в WebCore экспортируется только часть символов — в основном те, которые нужны WebKit.

Я изменил это, чтобы экспортировать каждый символ, и это каким-то образом вызвало эту ошибку.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top