添加新的继承"interface"和虚拟方法需要重新编译
Adding a new inherited "interface" and virtual methods require recompile
现有的答案涵盖了一般情况,但它们有点模糊,我需要确定这一点。
考虑:
- 从抽象基类"接口"派生的现有定义类
- 类是库的一部分,该库被编译为多个通过接口相互通信的dll
然后添加:
- 第二个"接口",定义的类现在将从中派生(所以现在它有两个接口)
- 新接口访问的已定义类的新虚拟方法
我需要重新编译链接此库的每个dll,还是只重新编译使用新方法的dll?
编辑:
我原来的接口公开了一个动态方法Dynamic(int OP, void* args)
。是否可以添加一个向新接口强制转换的op?
COM如何在不损坏现有接口的情况下向对象添加新接口?它是否堆叠接口、使用多重继承等???
让我来介绍一下它的工作方式。
静态链接库接口
In statically linked library
class Interface1
{
virtual Method1() = 0;
virtual Method2() = 0;
}
class NotReallyInterface2 : Interface1
{
virtual Method1() = 0;
virtual Method2() { // does something }
}
在dlls
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和ex正在构建同一个基类。因此,如果这些基本类的"副本"不同步,飞船就会坠毁。这意味着我不能更改NotReallyInterface2
中的单个方法签名。
如果没有人引用NotReallyInterface2
,这本可以奏效,我现在从答案中得到了这一点,整个事情都有意义。
您需要重新编译那些直接引用派生类的DLL。那些只通过接口引用它的人将继续工作。
COM依赖于这个东西。COM ABI规范有效地要求每个兼容的C++编译器不要以使接口停止工作的方式使用vtables。这就是为什么COM的一个基本要求是永远不要通过添加/删除/更改函数或为其提供新的基本接口来修改已发布的接口。
通过派生旧接口来添加新接口,并使实现类从新接口派生并不会破坏这一点;实现类中许多接口的多重继承也不起作用。
不那么抽象的类不应该成为障碍,但现在你已经超出了COM的保证范围。如果该类有数据成员,情况会更糟。我认为不重新编译代码仍然是安全的,但我不想再依赖它了。
官方表示,对任何类的任何更改都需要重新编译与该类相关的所有内容。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
{
...
};
这将(在大多数情况下)使vtable变长一点,这对代码的其他部分来说更容易接受,并且对于任何只使用Interface_a功能的代码来说,根本不会引起任何问题。[取决于编译器是否做了明智的工作-如果你这样做,标准仍然允许编译器"把一切都搞砸"。但我曾经在一家公司工作,那里有很多代码依赖于系统的其他部分"不变",我们对这类事情进行了大量分析和处理]。
添加一个新接口会添加额外的虚拟方法,从而更改vtable(内部编译器生成的表,用于路由虚拟方法调用)的布局。因此,您需要重新编译使用该类的每个模块(或至少重新编译在该类上创建/销毁或调用虚拟方法的每个模块)
假设第二个接口不重复来自第一个接口的任何方法完整签名(在这种情况下,您应该使用虚拟继承)并且在您的实现中,第二个界面实际上是第二个:
ImplClass : public Interface1, Interface2
您不需要重新编译只使用Interface1的现有代码。这是因为ImplClass现在将有两个指向两个vtable的指针,但第一个指针将保留在该类内存布局的开头。
此外,如果您在库中使用工厂方法,这意味着客户端代码总是通过调用库中实现的Interface1*CreateInterface1()方法来获得Interface*(并且从未直接处理ImplClass*),而现在该方法被重新编译,那么您甚至不关心接口顺序。
- 使用 MINGW gcc 编译时,不会为 std::string 调用重载的新运算符
- 如何编译:Mac上的Synergy(2017年的新工具链)
- 如何编译和运行一个新的 C++ Actor 框架项目?
- C++编译新的警告过滤器
- 每次都构建(make)lib,只有在lib较新时才重新编译项目
- 如何创建指向派生类的新指针,该派生类在C++编译时未知
- 如何在不退出和失去断点的情况下重新加载重新编译的二进制文件
- 在编译时将整数和分数部分宏组合成一个新的宏或双精度
- C++运算符新重载、编译错误
- 在新OSX上编译旧碳应用
- 代码块在尝试编译或创建新文件时出现"An assertion failed!"错误
- 在新的Visual Studio项目中包含任何Eigen 3.3.1文件将无法编译
- RCpp:如何在不重新启动R的情况下重新加载重新编译的C++代码
- 新安装的MinGW问题与编译和可执行文件
- 重新编译二进制类或派生类,以便在基类中添加新方法
- 添加新库以编译任何术语.如何编辑生成文件
- C++,如果其他新文件出现问题,使用g++进行编译可以很好地工作
- 每次添加新的源子文件夹时,Cmake都会重新编译所有内容
- 不完整类型的新内容在包装在模板中时编译
- 使用新的 Qt5 信号/插槽机制连接 QTcpSocket::error() 时编译错误