最近,我们在当前从VS2010移植到VS2015的遗留代码中遇到了一个有趣的效果。不幸的是,我无法创建一个小例子来显示这种效果,但我会尽可能准确地描述它。
我们有两个dll(我称它们为dll A和dll B)。dll A的项目定义了接口IFoo&一个派生接口IFxFoo
class __declspec(novtable) IFoo {
public:
virtual int GetType() = 0;
virtual ~IFoo() {}
};
class __declspec(novtable) IFxFoo : public IFoo {
public:
virtual int GetSlot() = 0;
};
在dll B中,两个接口都使用。
class CBImpl : public IFxFoo {
public:
...
void processFoo(IFoo* f) {
...
if (f->GetType() == IFXFOO) {
IFxFoo* fx = static_cast<IFxFoo>(f); //downcast
fill(fx);
}
}
void fill(IFxFoo* fx) {
m_slot = fx->GetSlot();
}
private:
int m_slot;
};
processFoo()将使用不同的IFoo实现进行调用。一些来自dll A,一些来自dll B。
现在发生的情况如下:-如果我们在编译dll B时打开了整个程序的优化,那么函数fill()中对虚拟函数GetSlot()的调用就会被Visual C++去虚拟化。这导致我们的程序崩溃。如果
- 全程序优化轮次
- 填土优化转弯
- 或者用__declspec(dllimport)/__declspec(dllexport)标记我们的接口
我现在的问题是:
- 我们的假设是正确的吗?去虚拟化是因为优化器在dll B中只看到了一个IFxFoo的实现,并假设这是唯一的实现,因为IFxFoos没有被标记为来自不同的dll
- 在头文件中创建"接口"的最佳方法是什么?我们过去常常这样做,但这似乎导致了一些问题
- 其他编译器(gcc/clang)是否表现出类似的行为
谢谢你的帮助Tobias
使用LTO会导致编译器对任何能够看到完整调用图的函数进行剧烈调整。
您所看到的是预期的,在需要从单独模块使用的函数上使用__declspec(dllexport)
或extern
,或将它们明确声明为DLL的一部分。def文件是解决问题的预期方法,因为编译器不再认为这些函数仅为内部函数。