现有的答案涵盖了一般情况,但它们有点模糊,我需要确定。
考虑:
然后添加:
我是否需要重新编译链接此库的每个dll,或仅重新编译使用新方法的dll?
修改
我的原始界面公开了一个动态方法Dynamic(int OP, void* args)
是否可以添加一个转换为新界面的操作?
COM如何在不破坏现有接口的情况下管理向对象添加新接口?它是堆栈接口,使用多重继承等吗???
让我说明一下它的工作方式。
静态链接库接口
In statically linked library
class Interface1
{
virtual Method1() = 0;
virtual Method2() = 0;
}
class NotReallyInterface2 : Interface1
{
virtual Method1() = 0;
virtual Method2() { // does something }
}
在dll中
In A.dll
Load statically linked library
class A : NotReallyInterface2
{
virtual Method1() { // does something }
}
In B.dll
Load statically linked library
class B: NotReallyInterface2
{
virtual Method1() { // does something different }
}
我想添加
class Interface3
{
virtual Method3() = 0;
}
我在这里遇到了一些问题,因为我的继承结构看起来像。
[a.dll [ library : Interface1 < NotReallyInterface2 ] < A ]
[b.dll [ library : Interface1 < NotReallyInterface2 ] < B ]
所以我害怕
[ a.dll [ library : Interface1 < NotReallyInterface2 ] < Interface3 < A ]
无效。
修改2
所以我发现了我的问题。显然,其他dll和可执行文件引用了我的NotReallyInterface2
。这意味着多个dll和exes正在构建相同的基类。因此,如果基类的那些“副本”不同步,那么就会失败。这意味着我无法在NotReallyInterface2
中更改单个方法签名。
如果没有人引用NotReallyInterface2
,这本来有用,我现在可以从答案中得到答案,整个事情都有意义。
答案 0 :(得分:4)
您需要重新编译直接引用派生类的DLL。只通过界面引用它的那些将继续工作。
COM依赖于这一点。 COM ABI规范有效地要求每个兼容的C ++编译器不要以使接口停止工作的方式使用vtable。这就是为什么COM的基本要求之一就是你永远不会通过添加/删除/更改功能或为其提供新的基础接口来修改已发布的接口。
通过派生旧接口并使实现类派生自新接口来添加新接口不会破坏它;实现类中的许多接口都没有多重继承。
不那么抽象的课不应该妨碍,但现在你不在COM的保证范围之内。如果该类有数据成员,则会更糟。我认为不重新编译代码仍然是安全的,但我不想再依赖它了。
答案 1 :(得分:2)
正式地说,对任何类的任何更改都需要重新编译触及该类的所有内容。 C ++标准和大多数编译器制造商的文档都没有对“如果你在课堂上改变某些事情会发生什么”提出任何保证。
在实践中,有些事情你可以做到这一点。你可以做的事情肯定会让一切都破裂。
第二个接口类将引入第二个vtable,这反过来意味着继承这两个类的类的差异。这几乎肯定是在“打破所有类别”中,它会在任何涉及类的“内容”的任何内容中引起问题。
是否可以添加一个从原始接口类派生的新类?
所以而不是:
class Interface_A
{
public:
virtual void func1();
virtual int func2();
...
};
class Interface_B
{
public:
virtual int func6();
...
};
class myClass : public Interface_A, public Interface_B
{
...
};
这样做:
class Interface_B : public Interface_A
{
public:
virtual int func6();
...
};
class myClass : public Interface_B
{
...
};
那会(在大多数情况下)jut使vtable更长一些,这对于其余代码来说更容易接受,并且对于只使用Interface_A功能的任何代码,都不会引起任何问题。 [根据编译器做出明智的工作 - 如果你这样做,标准仍然允许编译器“搞乱一切”。但我曾经在一家公司工作过,我们有很多代码依赖系统的其他部分“不改变”,我们分析并处理了很多这类事情。
答案 2 :(得分:1)
添加新接口会添加额外的虚拟方法,这些方法会更改vtable的布局(内部编译器生成的表以路由虚方法调用。)因此,您需要重新编译使用该类的每个模块(或至少每个创建/的模块)在类上销毁或调用虚方法。)
答案 3 :(得分:0)
假设第二个接口没有从第一个接口重复任何方法完全签名(在这种情况下你应该使用虚拟继承)并且在你的实现中第二个接口实际上是第二个:
ImplClass : public Interface1, Interface2
您不需要重新编译仅使用Interface1的现有代码。这是因为ImplClass现在有两个指向两个vtable的指针,但是第一个指针将保留在这个类内存布局的开头。
另外,如果您在库中使用了工厂方法,这意味着客户端代码总是通过调用类似于在库中实现的Interface1 * CreateInterface1()方法来获取Interface *(并且从不直接处理ImplClass *),现在这个方法得到了重新编译然后你甚至不关心接口顺序。