假设继承了一个复杂的代码库(在 Visual C++ 中,假设是 2003 年或更高版本),并且具有庞大而复杂的继承图。假设它很深,并且有很多虚函数,甚至可能还有多重继承。 (是的,有点维护噩梦)。任何将此类层次结构重构为更理智的尝试都需要知道每个类使用每个虚拟函数的哪个实现。
如果我们采用任意叶类 L1 - 它派生自基类 B1,而基类 B1 又派生自基类 B2,等等 - 它显然会有一个该类的 vtable,它将显示类似(伪 vtable)的内容:
L1::F1
B3::F2
B1::F3
L1::F4
etc.
...具体取决于哪个类已覆盖哪些虚函数。
如何以类似的形式看到这样的 vtable?可以通过阅读代码来手动重建它,但这很容易出错且费力。据推测,在调试器中闯入该类的对象可以让您通过该类的 vtable 指针检查 Watch 窗口中的 vtable,但这是一个尴尬的解决方案,特别是如果您还想查看 L2 的 vtable, L3,...LN。
DbgHelp.dll 是否提供以编程方式检查 vtable 的工具(允许以任何需要的形式输出)?或者还有其他方法吗?
在 Visual Studio 2005 中,有两个未记录的标志可以完全满足您的需要。 它们是 reportAllClassLayout 和 reportSingleClassLayout 标志。 例如,在 cl.exe 命令行上尝试“/d1 reportAllClassLayout”。 它将向您显示完整的班级布局,包括虚拟桌子,这是一个示例。另请参阅 https://blogs.msdn.microsoft.com/vcblog/2007/05/17/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022/ 没有太多有关这些标志的信息,因为它们目前尚未记录,但也许 Microsoft 会在 Visual Studio 的未来版本中正式支持它们。
另一种方法,实际上我更喜欢,是使用 IDA Pro 交互式反汇编程序。 有一个巨大的学习曲线,但 IDA 足够聪明,可以帮助您构建 VTable 并将它们链接到您的类。 它用于对传统上没有符号的二进制文件进行逆向工程,但它也使用 Visual Studio pdb 文件。 这样做,您将确切地看到所有您的vtable是什么样子的。 哪些虚拟表用于哪些方法,或者哪些方法被覆盖,同时单步执行代码。 换句话说,您实际上会在运行时调试期间看到您的方法调用被跟踪到“vtable”中。 正如您所注意到的,像 VS 调试器这样的典型调试器不会跟踪虚拟表。