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

Как вы можете видеть из кода ниже, у меня есть абстрактный базовый класс "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. Итак, я должен выяснить, почему 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 () правильный, с правильным vtable - довольно странно!

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

Спасибо, ребята, за помощь.

Ответы [ 5 ]

2 голосов
/ 12 января 2010

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

Так как вы не опубликовали достаточно кода, чтобы действительно разобраться, я думаю. Либо весь указатель this равен 0, либо указатель таблицы виртуальных функций равен 0, возможно, потому, что объект был удален после того, как он был создан и до того, как вы попытались вызвать его.

Лучший совет, который я могу вам дать, - создать намного меньшую пробирку. Либо вы найдете свою проблему, либо в конечном итоге приведете хороший пример.

vtbl не существует в экземпляре до конца процесса строительства. Фактически, спецификация требует прогрессивной модификации vtbl, чтобы соответствовать состоянию построения иерархии классов.

0 голосов
/ 27 января 2010

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

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

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

0 голосов
/ 13 января 2010

ssume your non-copyable выглядит следующим образом (по крайней мере, для моего)

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

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

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

наконец, ОБЯЗАТЕЛЬНО проверяйте наличие ошибки выделения. (да, «новым» является выделение по куче)

0 голосов
/ 12 января 2010

Я не знаком с вашей кодовой базой, но вы не должны писать следующее:

// 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
0 голосов
/ 12 января 2010

Можете ли вы опубликовать полный код?

После небольшого изменения в вашем коде (все, что доступно), оно работает:

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