【问题标题】:Inherited class "invalid pointer error" when calling virtual functions调用虚函数时继承类“无效指针错误”
【发布时间】:2011-01-04 04:32:02
【问题描述】:

正如您在下面的代码中看到的,我有一个抽象基类“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 问题。

感谢大家的帮助。

【问题讨论】:

  • 请使用复制和粘贴发布真实代码。
  • 这是真正的代码。我不能让它更简洁,因为它就在 WebKit 的中间。
  • 在调用 'WebCore::Chrome *chrome = new Chrome();' 之后,'chrome' 的值是多少?如果为NULL,是否抛出异常?
  • 不为空。我正在检查 gdb。
  • 请确保您的代码在发布之前可以正常工作。我们没有义务重新编写您的代码,以便在尝试解决您的问题之前进行编译...

标签: c++ inheritance polymorphism abstract-class abc


【解决方案1】:

我发现这是由于允许导出所有符号造成的。

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • 不幸的是,我做到了……什么是 Page 类? u 添加了代码 sn-p 没有细节供其他人理解
    【解决方案3】:

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

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

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

    直到构造过程结束时,vtbl 才会在实例中到位。事实上,规范要求逐步修改 vtbl 以匹配类层次结构的构造状态。

    【讨论】:

    • 您能详细说明一下吗?我对 C++ 很陌生。我确实有一个实例,Chrome,我可以调用一些函数——比如 focus(),但不能调用 ABC 中定义的函数,比如 repaint()。
    • 'Chrome' 不是实例,而是类。 'chrome' 是实例。
    • vtable 为 nil - 请参阅更新。我不知道它是怎么发生的。
    • 你在创建后删除它,在使用它之前是通常的方法。
    【解决方案4】:

    我不熟悉您的代码库,但您不应该编写以下内容:

    // 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
    

    【讨论】:

    • 是的,这是示例中的拼写错误。
    • @Alex:这就是为什么你应该总是剪切/粘贴代码。 永远不要重新输入,因为它只会增加错误并导致帮助您的人忙于 null 工作。
    【解决方案5】:

    你能发布完整的代码吗?

    在您的代码稍作修改后(无论是什么可用的),它都可以工作:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-23
      • 1970-01-01
      • 1970-01-01
      • 2016-06-24
      • 1970-01-01
      • 2011-02-11
      相关资源
      最近更新 更多