为什么新的虚函数不会破坏每种现象的二进制兼容性?

why new virtual function will not break binary compatibility per phenomenon?

我正在学习基于此 KDE wiki 的二进制兼容性,并看到

add a virtual function to a class that doesn't have any virtual functions or virtual bases

会破坏兼容性。然后我试了一下。

假设我要创建一个FastString.dll来提供出来,这里是定义。

//FastString.h

#ifdef _DLL_EXPORT
#define LIB_EXPORT __declspec(dllexport)
#else
#define LIB_EXPORT __declspec(dllimport)
#endif

class LIB_EXPORT FastString
{
public:
    FastString(void);
    ~FastString(void);

    int length();

private:
    int m_len;
    unsigned char *m_bytes;
};

和实施

//FastString.cpp
#include "FastString.h"

FastString::FastString(void)
{
    m_len = 0;
}

FastString::~FastString(void)
{
}

int FastString::length()
{
    printf("Function of length, string len is %d\n", m_len);
    return m_len;
}

在第三个 exe 文件 test.exe 中,使用如下 FastString

// main.cpp

#include <conio.h>
#include "FastString.h"

int _tmain(int argc, _TCHAR* argv[])
{
    FastString str;
    str.length();

    printf("Please input any key to exit...");
    _getch();
    return 0;
}

请注意:main.cpp中的包含的FastString.h是另一个文件,我在FastString中添加虚函数时,修改在FastString.dll。

它们在同一个解决方案中(编译器:VS2012),并且构建成功。之后,我在 FastString.h 中添加了一个新的虚函数。

virtual bool isEmpty();

而在 FastString.cpp 中,我用简单的 return

实现了它
bool FastString::isEmpty()
{
    return false;
}

然后我单独构建 FastString.dll,然后重新运行 test.exe。输出与上一个相同,没有任何错误。
那么,为什么这种行为没有破坏二进制兼容性?
根据我的理解,实例 str 应该有一个 vtable 指针,并且内存布局必须已更改。
我也有一个基于VS工具的调试,发现str仍然没有_vptr,这是否意味着vtable是在下面创建的编译器时期,不在link时期?

non-virtual 函数 FastString::length() 的调用与布局无关,在 DLL 中定义的函数知道实际的对象布局并找到正确的成员。通过制作 m_len public 并从 DLL 外部访问它,您应该会遇到布局不兼容问题。

虽然没有关于 VMT 所在位置的通用规则,但它通常由编译器创建并放入某个特定的翻译单元 (*.obj) 中,即第一个虚函数定义的位置。有时,例如所有虚函数都是内联的,必须应用更高级的策略,它们通常涉及链接器。但通常在使用较旧的头文件时,编译器将没有足够的提示涉及 VMT 的存在,并且不会为该对象创建 VMT 指针。