2011-09-08 29 views
2

我有一个库可以编译为共享库(或Windows中的DLL)。它有一个派生自另一个库中另一个类的类。基类有一些虚拟方法,我的类覆盖了其中的一些。例如:虚拟覆盖和二进制兼容性

class Base { 
public: 
    virtual void method1(); 
    virtual void method2(); 
    virtual void method3(); 
}; 

class Derived: public Base { 
public: 
    virtual void method2(); 
}; 

现在我发现其中一个虚拟方法对我的班级来说并不起作用。目前,它不重写此方法,所以我要重写它也解决其行为:

class Derived: public Base { 
public: 
    virtual void method2(); 
    virtual void method3(); 
}; 

这会不会打破与旧版本我的媒体库的二进制兼容性?

就我所知,它与添加虚拟函数不同,因为vtable中虚拟方法的数量和顺序保持不变。唯一的区别是,我的类的vtable中的特定条目现在将包含不同的值。它是否正确?

我也很确定,目前使用我的库的应用程序都没有使用该方法,因为它完全被破坏并且从未工作。所以我不担心打破对基本方法实现的现有调用。我只是想确保我不会破坏别的东西。

+0

逻辑上,它听起来应该没问题。但我不确定在C++中维护二进制兼容性的确切规则。 – Mysticial

回答

3

由于您在谈论DLL,我认为这是Visual Studio/Windows中的C++。添加覆盖不会破坏二进制兼容性,因为vtable的大小没有变化。但是,如果您不重新编译实例化Derived的新实例的所有代码,则可能会导致一些不良结果。这是因为vtable由实例化源初始化,而不是实现Derived的类的源。

+0

不,它是跨平台的,不仅仅是Windows,我滥用了DLL术语。无论如何,是不是vtable只是某种形式的DLL中的方法指针的静态数组?我认为vtable指针是由构造函数初始化的。为什么实例化源对这个过程有任何影响? –

+0

vtable不仅仅是一个静态数组,因为它实际上改变了对象的生命周期。在基类/派生类构造函数/析构函数内的调试器中查看它,您将看到方法指针根据派生类是否已构造/破坏而改变。 –

+0

在构造函数被调用之前,vtable被初始化。它不能被声明类的模块分配,因为该类(以及从它派生的任何类)不知道表的最终大小,因为可能会有衍生工具添加新的虚拟。所以,它必须由实例化对象的模块分配。 –

0

如果我理解正确,你有你的基类在Dll1和你的派生类在Dll2。如果是这种情况,您所描述的更改不会影响Dll1。假设您将安装更新的Dll2,则只要通过指针或对Base的引用访问Derived的实例,您的应用程序就会切换到调用Derived :: method3()。

0

您的派生类(即添加了新的虚拟方法)存在的库而不是必须与库的旧版本兼容(ABI)。这是因为在添加重写的虚拟方法时,您无法控制编译器如何生成vtable。

+0

但我认为任何编译器都必须生成一个与基类兼容的vtable?那个vtable保持不变,我不(也不能)改变它。 –