当前位置: 首页 > 知识库问答 >
问题:

“纯虚拟函数调用”在gcc 4.4上,但在较新版本或clang 3.4上没有

羊舌墨一
2023-03-14

我有一个MCVE,它在我的一些机器上使用g版本4.4.7编译时崩溃,但确实适用于clang版本3.4.2和g版本6.3。

我想知道它是来自未定义的行为还是来自这个古老版本的gcc的实际错误。

#include <cstdlib>

class BaseType
{
public:
    BaseType() : _present( false ) {}
    virtual ~BaseType() {}

    virtual void clear() {}

    virtual void setString(const char* value, const char* fieldName)
    {
        _present = (*value != '\0');
    }

protected:
    virtual void setStrNoCheck(const char* value) = 0;

protected:
    bool _present;
};

// ----------------------------------------------------------------------------------

class TypeTextFix : public BaseType
{
public:
    virtual void clear() {}

    virtual void setString(const char* value, const char* fieldName)
    {
        clear();
        BaseType::setString(value, fieldName);
        if( _present == false ) {
            return; // commenting this return fix the crash. Yes it does!
        }
        setStrNoCheck(value);
    }

protected:
    virtual void setStrNoCheck(const char* value) {}
};

// ----------------------------------------------------------------------------------

struct Wrapper
{
    TypeTextFix _text;
};

int main()
{
    {
        Wrapper wrapped;
        wrapped._text.setString("123456789012", NULL);
    }
    // if I add a write to stdout here, it does not crash oO
    {
        Wrapper wrapped;
        wrapped._text.setString("123456789012", NULL); // without this line (or any one), the program runs just fine!
    }
}
g++ -O1 -Wall -Werror thebug.cpp && ./a.out
pure virtual method called
terminate called without an active exception
Aborted (core dumped)

这实际上是最小的,如果删除此代码的任何功能,它就会正确运行。

当使用-O0编译时,代码片段可以正常工作,但是对于GnuCC上定义的-O1的每个标志,使用-O0标志编译时仍然可以正常工作留档。

生成核心转储,可以从中提取回溯:

(gdb) bt
#0  0x0000003f93e32625 in raise () from /lib64/libc.so.6
#1  0x0000003f93e33e05 in abort () from /lib64/libc.so.6
#2  0x0000003f98ebea7d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib64/libstdc++.so.6
#3  0x0000003f98ebcbd6 in ?? () from /usr/lib64/libstdc++.so.6
#4  0x0000003f98ebcc03 in std::terminate() () from /usr/lib64/libstdc++.so.6
#5  0x0000003f98ebd55f in __cxa_pure_virtual () from /usr/lib64/libstdc++.so.6
#6  0x00000000004007b6 in main ()

请随时在评论中询问测试或详细信息。问:

>

  • 是实际的代码吗?是的!是的!字节对字节。我已经检查并重新检查。

    您使用的GnuCC du的确切版本是什么?

    $ g++ --version
    g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)
    Copyright (C) 2010 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    

    我们可以看到生成的程序集吗?是的,这是在 pastebin.com

  • 共有2个答案

    魏翰
    2023-03-14

    虽然这个错误的真正解决方案是不使用RedHat GnuCC 4.4.7(或任何RedHat编译器…),但我们暂时只能使用这个版本。

    我们确实找到了另一种选择:将BaseType的构造函数混淆到编译器,从而防止它过度优化它。我们简单地通过在单独的翻译单元中定义BaseType::BaseType()来做到这一点。

    这样做绕过g错误。我们确实检查了BaseTypeTypeTextFix虚拟表指针在调用其相关构造函数之前都被写入构造对象。

    巫马浩言
    2023-03-14

    这是一个特定于红帽的错误,在FSF GCC中不存在。这不是代码中的问题。

    在同时安装了CentOS 6的GCC和FSF GCC 4.4.7的系统上,生成了汇编列表并查看了两者之间的差异,有一点值得一提:

    CentOS 6的GCC生成

    movq $_ZTV8BaseType+16, (%rsp)
    

    而FSF GCC 4.4.7生成

    movq $_ZTV11TypeTextFix+16, (%rsp)
    

    换句话说,Red Hat的一个GCC补丁使其错误地设置了vtable。这是您的main函数的一部分,您可以在之后不久在您自己的程序集列表中看到它。L48:

    Red Hat在其GCC版本中应用了许多补丁,其中一些是影响代码生成的补丁。不幸的是,其中一个似乎有一个意想不到的副作用。

     类似资料:
    • 在C 11中,我们能够声明一个析构函数是自动生成的: 此外,我们可以将析构函数声明为纯虚: 我的问题是:如何将析构函数声明为自动生成和纯虚拟?看起来以下语法不正确: 这一个也不是: 也不是这个: 编辑:关于问题目的的一些澄清。基本上,我希望一个空类是不可实例化的基类,但派生类是可实例化的,那么该类必须有一个纯虚拟析构函数。但另一方面,我不想在.cpp文件中提供定义。因此,我需要某种与等效的机制。我

    • 我用不同的版本和代码更新了一个应用程序。然后通过“右键点击->Android Tools->Export signed application Package”使用以前版本apk的相同密钥库对apk进行签名。但在上传到google play时,却出现了以下错误。谁能告诉我我哪里不对劲吗? “上载失败您将一个使用不同证书签名的APK上载到以前的APK上。您必须使用相同的证书。您现有的APK使用带有指

    • 在构建一个插件时,我会遇到一个“找不到类”的异常 有人已经设法创建了插件或类加载器XPages就这样杀死了?

    • 本文向大家介绍C++中虚函数与纯虚函数的用法,包括了C++中虚函数与纯虚函数的用法的使用技巧和注意事项,需要的朋友参考一下 本文较为深入的分析了C++中虚函数与纯虚函数的用法,对于学习和掌握面向对象程序设计来说是至关重要的。具体内容如下: 首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承、动态绑定。通过数据抽象,可以使类的接口与实现分离,使

    • 问题内容: 我收到以下错误 尝试在空对象引用上调用虚拟方法’void android.widget.StackView.setAdapter(android.widget.Adapter)’ 在这条线上 完整的片段 EventsFragment.java 是 Stack_Adapter.java Stack_Items 问题答案: 您正在执行: 你是。返回。 你为什么用? 在哪里?您应该从正确的x

    • 我的应用程序有问题,想解决它,但我无法访问解决方案,请帮帮我,,