我有一个静态方法的类看起来大致如下:
class X {
static float getFloat(MyBase& obj) {
return obj.value(); // MyBase::value() is virtual
}
};
我用MyDerived的一个实例调用它,它是MyBase的子类:
MyDerived d;
float f = X::getFloat(d);
如果我将包含X的obj文件链接到我的可执行文件中,一切都按预期工作。如果我期望得到3.14,我就明白了。
如果我创建一个包含.lib中的X.obj文件和链接的.lib,它会中断。当我调用getFloat()时,它返回 -1。#IND00 。这是某种类型的哨兵值,应该告诉我这里有什么问题吗?
当你直接链接lib而不是obj时,有什么不同吗?
我没有收到任何编译器警告或错误。
修改:
我在Windows XP Pro SP3上使用Visual Studio 2005。为了确保我没有链接旧文件,我将value()方法克隆到一个新的value2()方法中并调用它。行为是一样的。
编辑#2:
所以,如果我使用我的调试器跟踪调用,我发现它根本不会进入我的value()方法。相反,它将进入一个不同的(不相关的)方法。这让我觉得我的vtable已经损坏了。我认为我所看到的行为必然是其他一些问题的副作用。
解决了!(感谢Vlad)
事实证明我违反了一个定义规则(ODR),尽管从我发布的代码中看不出来。 This是来自Visual C ++人员的一篇很棒的文章,解释了这个问题以及一种跟踪问题的方法。 / d1reportSingleClassLayout 编译器标志是一个很棒的学习工具。
当我在两个不同的项目中抛弃MyBase和MyDerived的我的类布局时,我发现调用代码和库代码之间存在差异。事实证明我的头文件中有一些 #ifdef 块,相应的 #define 语句位于主项目的预编译头中,但不在子项目中(库中) )。我是否曾提到我认为预处理器宏是多么邪恶?
无论如何,我只发布这些东西,因为它可能对其他人有帮助。 This question对我也很有帮助。
答案 0 :(得分:1)
由于lib只是一个容器,如果你在两种情况下链接相同的.obj文件,那么Brian说他们不应该(不能?)是一个区别。
要注意的一件事是,如果您更改了MyBase的定义,您显然需要使用它重新编译库和代码。例如,如果您在值方法之前向MyBase添加了一个新的虚拟方法,那么这会弄乱库,因为v-table的值偏移量会有所不同。
答案 1 :(得分:1)
使用 MyDerived
类的不同定义(即.h
/ {{1的不同版本)编译lib和可执行文件时,会发生此问题声明.hh
的/ .hpp
文件。完全清理并重建你的项目。除此之外,不同的编译器选项可以负责,尽管有点不太可能。
如果从头开始重建所有内容后问题仍然存在,那么通过在库中实例化MyDerived
内的虚拟MyDerived
对象来确定它。使用调试器比较虚拟getFloat
的{{1}}(在库中实例化)和作为参数传递的vtable
对象引用的MyDerived
(在可执行文件中实例化)。 )有些东西应该立刻吸引眼球。
答案 2 :(得分:0)
应该没有区别。只需确保您所包含的.h文件对应于您正在链接的.lib。我怀疑你可能正在链接到一个旧的.lib文件。
如果您使用的是visual studio,而不是显式指定.lib文件,只需右键单击该项目并将依赖项设置为.lib项目即可。这样你就可以确定它使用正确的.lib文件。