1

正如您在下面的代码中看到的,我有一个抽象基类“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 显示它为空;来自 GDB:

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

一个普通的实例有一个正确的 vtable。所以,我必须弄清楚为什么 vtable 是 nil - 我想知道这怎么会发生?也许是因为它在其他一些构造函数类中被实例化?

更新 3: 看起来我对问题是正确的,因为它是在另一个类的构造函数中实例化。

所以,在实例化之前看起来像这样:

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

并且 m_chrome 是一个无效的实例,具有一个 nil vtable。我已经更改了实例化,因此它在第一次需要变量时发生(这涉及保存 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: 最后一次更新,我保证 :)。好的,所以我已经准确地指出了它。如果您在 Page 构造函数的主体中实例化它,您将使用 vtable 获得正确的实例。如果您在 Page 构造函数的头部实例化它,它就没有 vtable。您可以在构造函数的头脑中进行的变量设置类型是否有任何限制?我想这是另一个 Stackoverflow 问题。

谢谢你们这么有帮助。

4

5 回答 5

2

是的,“this”指针为零。加 8 得到一个偏移量,这是你的错。您显然根本没有任何实际对象。

由于您没有发布足够的代码来真正掌握,我猜。要么整个​​ this 指针为 0,要么虚函数表指针为 0,可能是因为对象在创建后且在您尝试调用它之前已被删除。

我能给你的最好建议是制作一个小得多的试管。要么你会发现你的问题,要么你最终会得到一个可发布的例子。

在构造过程结束之前,vtbl 在实例中并不存在。事实上,规范要求逐步修改 vtbl 以匹配类层次结构的构造状态。

于 2010-01-12T13:44:33.297 回答
0

你能发布完整的代码吗?

在您的代码稍作修改后(任何可用的),它可以工作:

#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;
}
于 2010-01-12T14:16:48.410 回答
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
于 2010-01-12T15:08:08.510 回答
0

假设您的不可复制如下(至少对我而言)

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

在将public修饰符插入到类 chrome 的函数和一些虚拟实现之后,整个事情都没有说明问题。

发布的代码没有问题,可能是你做错了,没有在这里发布这些部分。

最后,检查分配失败。(是的,“新”是堆上的分配)

于 2010-01-13T03:19:21.967 回答
0

我发现这是由允许导出所有符号引起的。

通常,WebCore 只导出了一个符号子集——基本上是 WebKit 需要的东西。

我将其更改为导出每个符号 - 它以某种方式导致了此错误。

于 2010-01-27T13:00:22.593 回答