在不同项目(没有编译时链接)使用的标头中声明的纯抽象C++类是否共享相同的虚拟表模型

Does a pure abstract C++ class declared in a header used in different projects (without compile time linking) share the same Virtual Table model?

本文关键字:抽象 C++ 声明 共享 模型 虚拟 是否 编译 项目 链接      更新时间:2023-10-16

我有一个C++头,声明了一个仅由纯虚拟方法组成的类。我有两个使用该标头的 DLL(其中一个实现该接口(,但在编译时未链接。一个 DLL 动态加载另一个 DLL,将实现的接口的指针传递给另一个。这些 DLL 是否共享相同的虚拟表结构?

当然,类头足以构建完整的类(这里讲的是内存中的布局,所有内容是如何定位的,而不是其中的实际数据(,包括精确的虚拟表结构。

想想看,每个链接对象(您的.cpp文件(都是单独编译的,只有头文件是通用的,但在编译时,编译器必须知道虚拟表的精确结构才能正确路由虚拟调用。

便通读一个问题...

如果我理解正确,你会得到这样的东西:

class A
{
public:
    virtual void foo()=0;
}

B.cpp

#include <A.h>
class B : public A
{
public:
    void foo()
    {}
}

C.cpp

#include <A.h>
void bar(A * a)
{}

所以B.cpp有一个实现A的类,C.cpp有一些函数接受指向A实例的指针(你提供了B的实例(。 这是对的吗?

如果是这样,那么是的,这些共享一个 vtable。 vtable是通过编译类B创建的,C.cpp根本没有自己的vtable。

你很安全。

方法在vftable中出现的顺序由基类结构决定,这就是您应该关心的全部内容。但这是特定于编译器的,因此使用相同的编译器来生成 dll。不要依赖它们向后兼容(或至少检查文档(。

假设您有以下标头:

//header.h
class A
{
public:
    virtual void foo() = 0;
    virtual void goo() = 0;
};

并且您B.dll以下类:

class B : public A
{
public:
    virtual void foo() {}
    virtual void goo() {}
}

现在,在 X.dll 中,您会收到一个指向A的指针,该指针是在 B.dll 中创建的B对象。

电话会议

void test( A* a )
{ 
   a->foo();
}

将调用B::foo() .

你可以尝试的一个简洁的实验是用header.h编译B.dll,当你编译X.dll时,反转方法的顺序header.h

//header.h
class A
{
public:
    virtual void goo() = 0;
    virtual void foo() = 0;
};

在这种情况下,尽管您永远不应该这样做,但在 X.dll 中对 test() 的相同调用可能会调用该方法B::goo() 。这是因为X.dll假定标头中存在vftable。不过,这是未定义的行为;我写这个例子只是为了说明一个观点。

这完全取决于编译器,但通常,当一个类是纯虚拟的并且没有在翻译单元中定义任何成员函数时,编译器将生成vtable作为符号。在您的特定情况下,不同的翻译单元将为基类型生成单独的完全相等的vtable,链接器/加载器将丢弃除一个符号之外的所有符号,就像丢弃任何其他弱符号一样(想想模板化的非内联函数(。

但请注意,vtable的生成不是标准化的,这意味着如果您混合来自两个不同编译器甚至版本的代码,可能会导致 ODR 冲突。