3

我有一个带有静态方法的类,大致如下:

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,我明白了。

如果我创建一个包含 X.obj 文件并在 .lib 中链接的 .lib,它会中断。当我调用 getFloat() 时,它返回-1.#IND00。这是某种类型的哨兵值,应该告诉我这里出了什么问题吗?

当您在 lib 中链接而不是直接在 obj 中链接时,有什么不同吗?

我没有收到任何编译器警告或错误。

编辑:
我在 Windows XP Pro SP3 上使用 Visual Studio 2005。为了确保我没有链接旧文件,我将 value() 方法克隆到一个新的 value2() 方法中并改为调用它。行为是一样的。

编辑#2:
因此,如果我使用调试器跟踪调用,我发现它根本不会进入我的 value() 方法。相反,它进入了一种不同的(不相关的)方法。这让我觉得我的 vtable 已损坏。我认为我看到的行为一定是其他问题的副作用。


解决了!(感谢弗拉德)
事实证明我违反了单一定义规则(ODR),尽管从我发布的代码中并不明显。是来自 Visual C++ 人员的一篇很棒的文章,它解释了这个问题以及一种追踪它的方法。/d1reportSingleClassLayout编译器标志是一个很棒的学习工具。

当我在两个不同的项目中为 MyBase 和 MyDerived 倾倒我的类布局时,我发现调用代码和库代码之间存在差异。事实证明,我的头文件中有一些#ifdef块,相应的#define语句位于主项目的预编译头文件中,但不在子项目(库)中。我有没有提到我认为预处理器宏有多邪恶?

无论如何,我只是发布这些东西,因为它可能对其他人有帮助。这个问题对我也很有帮助。

4

3 回答 3

1

由于 lib 只是一个容器,如果您在两种情况下都链接相同的 .obj 文件,那么正如 Brian 所说,它们不应该(不能?)有区别。

需要注意的一件事是,如果您更改了 MyBase 的定义,您显然需要重新编译库和使用它的代码。例如,如果您在 value 方法之前向 MyBase 添加了一个新的虚拟方法,那么这会弄乱库,因为 value 的 v-table 偏移量会不同。

于 2009-03-07T18:23:44.990 回答
1

当使用不同的类定义MyDerived编译 lib 和可执行文件时会出现此问题(即声明 . 的 // 文件.h的不同版本。完全清理并重建您的项目。除此之外,不同的编译器选项可能是负责任的,但这有点不太可能。.hh.hppMyDerived

如果从头开始重建所有内容后问题仍然存在,则通过在库中实例化一个虚拟MyDerived对象来解决问题。getFloat使用调试器比较vtabledummy MyDerived(在库中实例化)和作为参数传递vtableMyDerived对象引用(在可执行文件中实例化)。有些东西应该立即浮现在眼前。

于 2009-03-07T20:28:52.707 回答
0

应该没有区别。只需确保您包含的 .h 文件与您要链接到的 .lib 完全对应。我怀疑您可能正在链接到旧的 .lib 文件。

如果您使用的是 Visual Studio,而不是显式指定 .lib 文件,只需右键单击项目并将依赖项设置为 .lib 项目。这样您就可以确保它使用正确的 .lib 文件。

于 2009-03-07T17:51:58.297 回答