【问题标题】:Change pure virtual to virtual and stay binary compatible将纯虚拟更改为虚拟并保持二进制兼容
【发布时间】:2011-10-08 14:25:41
【问题描述】:

我可以将纯虚拟函数(在基类中)更改为非纯函数,而不会遇到任何二进制兼容性问题吗? (Linux、GCC 4.1)

谢谢

【问题讨论】:

    标签: c++ binary-compatibility pure-virtual


    【解决方案1】:

    当您从纯virtual 切换到virtual 然后重新编译代码时,没有兼容性问题。 (但是,virtual 到纯 virtual 可能会导致问题。)

    唯一需要注意的是,非纯 virtual 方法必须有一个主体。它们不能保持未实现。即

    class A {
    public:
      virtual int foo ()
      {
        return 0; //put some content
      }
    };
    

    你不能简单地说,

    virtual int foo();
    

    即使你不使用它也会导致链接器错误。

    【讨论】:

    • 这不是很安全,这违反了 ODR(不同的翻译单元对同一类型有不同的定义)并且在某些情况下可能会导致问题。
    • @David,你能举个例子吗?
    • 我想我在我给出的答案中提供了,但我会尽量简化。如果所有虚拟方法都是纯虚拟方法或在类定义中内联定义,则不存在创建 vtable 的单个翻译单元,而是包含该标头的所有翻译。 vtable 将被标记为弱符号,链接器将随机选择其中之一。如果您的代码依赖于可实例化的基类,该基类调用(对于此翻译单元而言不是纯的)虚函数,并且链接器选择了一个该函数为纯的 vtable...
    • ... 然后代码将调用什么(根据 vtable)是纯虚函数,通常由打印消息并终止应用程序的 thunk 处理。 ODR 违规的问题在于它们不需要被诊断,而且在许多情况下,编译器确实很难这样做,如果它进入产品,问题是代码的不同部分会有对物体的不同解释,当这些解释不匹配时,你就有了灾难的秘诀。
    【解决方案2】:

    保持二进制兼容性对您意味着什么?

    对象布局将是相同的,但您将违反单一定义规则除非您重新编译所有代码,此时二进制兼容性基本上是无用的。如果不重新编译,ODR 就会被破坏,虽然它可能会工作,但也可能无法工作。

    特别是如果类中的所有虚方法都是纯的或内联定义的,那么编译器可能会在每个包含标头的翻译单元中生成 vtable 并将其标记为弱符号。然后链接器将选择其中一个并丢弃所有其他。在这种情况下,链接器不需要验证所有 vtable 是否完全相同,并且会随机选择一个(或以未定义的方式确定性地),并且它可能会选择一个这样的 vtable,其中该方法是纯虚拟的,在如果在基类的对象上调用该方法,turn 可能最终导致应用程序崩溃。

    【讨论】:

    • 在我的例子中,并不是所有的虚拟方法都是纯的,有一种混合。我想无论如何我都会重建一切,而不是忍受不确定性。
    • 特别是我有 N 个共享库。我只是通过第一个库中的基类指针调用虚函数;其他 N-1 个库包含具有该函数重新实现的派生类。我打算把纯的改成非纯的,只重新编译第一个库。
    猜你喜欢
    • 2011-11-13
    • 2013-01-30
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    相关资源
    最近更新 更多