如果使用不常见的 class,vtable 是否会在二进制文件中持续存在?

Does the vtable persist among binaries if using a non-common class?

假设我有三个库,都是用 C++ 编写的:

  1. logging.a:辅助日志记录的静态库。它定义了一个抽象 class,其他 classes 可以使用它来自定义他们的日志记录(通过实现纯虚方法 virtual void CustomLogger::customLogging(std::string) = 0)。
  2. libmainproject.so:主库。它包含一个 class,不为外界所知,(CustomLoggerImpl) 扩展了 CustomLogger 并实现了自定义日志记录方法。
  3. libplugin.so: 一个插件库,需要记录内容并希望将其日志记录与 libmainproject.
  4. 中的记录集成

libmainprojectlibplugin 之间的接口是 C 接口,尽管这两个库的内部都是用 C++ 编写的。

libplugin 不知道 libmainproject 中存在 CustomLoggerImpl,但知道 logging.[=33 中的接口 CustomLogger =]

为了使用来自 libplugin 的自定义日志记录,我已经将指向 CustomLoggerImpl 的指针作为 void * 传递(因为它发生在 C 接口上,不能使用自定义对象).

一旦我需要在插件中登录,我就将 void *(指向 CustomLoggerImpl 对象)转换为 CustomLogger *(Liskov 替换原则)。然后我调用 customLogger->customLogging("whatever");.

虽然没有崩溃,但调用似乎没有到达 libmainproject。为什么会这样? 我假设问题是 vtable 存在一些问题,因此它不能从没有对象定义的不同二进制文件正常工作,但想要一些更深入地解释为什么会发生这种情况。

标准 C++ 未定义动态链接(共享)库,因此您处于特定于实现的行为中。

实施通常会做合理的事情;假设 CustomLogger 在所有二进制文件中具有相同的 vtable 布局是合理的。该标准甚至没有规定 vtable,因此这不是一个硬性声明。

支持动态链接库的实现通常不会假设所有代码都存在于一个二进制文件中。在 Windows 上,MSVC 做的假设较少,因为它有一个 __declspec(dllimport) 扩展,专门告诉编译器有代码来自另一个二进制文件。但是你的问题清楚地表明你没有使用 MSVC。您的编译器不会假设 customLogging 覆盖必须在 libplugin.so 中定义,只是因为 libplugin.so 调用 customLogging.

因此,我同意 VTT。这里最大的问题似乎是你通过 void*CustomLoggerImpl* 转换为 CustomLogger*,它应该在什么地方 CustomLoggerImpl* -> CustomLogger* -> void* -> CustomLogger*。到 base class 的转换应该在 C++ 代码中显式发生。