为什么新的虚函数不会破坏每种现象的二进制兼容性?
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 指针。
我正在学习基于此 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 指针。