gpt4 book ai didi

c++ - 了解多继承的vptr?

转载 作者:IT老高 更新时间:2023-10-28 22:12:35 26 4
gpt4 key购买 nike

我试图使本书有效的c++中的语句有意义。以下是多重继承的继承图。

现在,这本书说vptr在每个类中都需要单独的内存。也有以下陈述
上图中的一个奇怪之处是,即使涉及四个类,也只有三个vptr。 如果愿意,实现可以自由生成四个vptr,但是三个就足够了(事实证明B和D可以共享一个vptr),并且大多数实现都利用此机会来减少编译器生成的开销。
我看不到任何理由为什么在vptr的每个类中都需要单独的内存。我了解vptr是从基类继承的,无论继承类型是什么。如果我们假设它显示了继承的vptr的结果内存结构,那么他们如何使该语句

B and D can share a vptr


有人可以澄清一下多重继承中的vptr吗?
  • 每个类中是否都需要单独的vptr?
  • 同样如果上述情况成立,为什么B和D可以共享vptr?
  • 最佳答案

    您的问题很有趣,但是我担心您的首要目标是太大,因此,如果您不介意,我将分几个步骤进行回答:)

    免责声明:我不是编译器作者,尽管我当然已经研究了该主题,但请谨慎使用。我会出现错误。而且我不太熟悉RTTI。而且,由于这不是标准的,所以我描述的是可能性。

    1.如何实现继承?

    注意:我将忽略对齐问题,它们只是意味着在块之间可以包含一些填充

    现在,让我们省略虚拟方法,并集中在下面实现继承的方式。

    事实是继承和构成有很多共同点:

    struct B { int t; int u; };
    struct C { B b; int v; int w; };
    struct D: B { int v; int w; };

    看起来像:
    B:
    +-----+-----+
    | t | u |
    +-----+-----+

    C:
    +-----+-----+-----+-----+
    | B | v | w |
    +-----+-----+-----+-----+

    D:
    +-----+-----+-----+-----+
    | B | v | w |
    +-----+-----+-----+-----+

    令人震惊的是:)?

    但是,这意味着,多重继承要简单得多:
    struct A { int r; int s; };
    struct M: A, B { int v; int w; };

    M:
    +-----+-----+-----+-----+-----+-----+
    | A | B | v | w |
    +-----+-----+-----+-----+-----+-----+

    使用这些图,让我们看看将派生指针转换为基本指针时会发生什么:
    M* pm = new M();
    A* pa = pm; // points to the A subpart of M
    B* pb = pm; // points to the B subpart of M

    使用我们之前的图表:
    M:
    +-----+-----+-----+-----+-----+-----+
    | A | B | v | w |
    +-----+-----+-----+-----+-----+-----+
    ^ ^
    pm pb
    pa
    pb的地址与 pm的地址略有不同的事实是由编译器自动通过指针算术处理的。

    2.如何实现虚拟继承?

    虚拟继承非常棘手:您需要确保所有其他子对象都共享一个 V(用于虚拟)对象。让我们定义一个简单的菱形继承(钻石问题)。
    struct V { int t; };
    struct B: virtual V { int u; };
    struct C: virtual V { int v; };
    struct D: B, C { int w; };

    我将省略表示,并集中精力确保 D对象中的 BC子部分共享同一子对象。如何做呢 ?
  • 请记住,类大小应为常数
  • 请记住,在设计时,B和C都无法预见它们是否将一起使用

  • 因此,找到的解决方案很简单: BC仅为 V的指针保留空间,并且:
  • 如果您构建一个独立的B,则构造函数将在堆上分配一个V,它将自动处理
  • 如果您将B构建为D的一部分,则B子部分将期望D构造函数将指针传递至V的位置

  • 显然是 C的思想。

    D中,优化使构造函数可以在对象中为 V保留空间,因为 D不会实际上从 BC继承,给出您所显示的图(尽管我们还没有虚拟方法)。
    B:  (and C is similar)
    +-----+-----+
    | V* | u |
    +-----+-----+

    D:
    +-----+-----+-----+-----+-----+-----+
    | B | C | w | A |
    +-----+-----+-----+-----+-----+-----+

    现在,请注意,从 B转换为 A的过程比简单指针算法要复杂一些:您需要遵循 B中的指针,而不是简单指针算法。

    但是,还有一个更糟糕的情况,即向上转换。如果我给你一个指向 A的指针,你怎么知道如何回到 B

    在这种情况下,魔术是由 dynamic_cast执行的,但这需要存储在某处的某些支持(即信息)。这就是所谓的 RTTI(运行时类型信息)。 dynamic_cast将首先通过某种魔术来确定 AD的一部分,然后查询D的运行时信息以知道 D子对象存储在 B中的何处。

    如果我们没有 B子对象,它将返回0(指针形式)或引发 bad_cast异常(引用形式)。

    3.如何实现虚拟方法?

    通常,虚拟方法是通过每个类的v表(即指向函数的指针表)实现的,并且通过每个对象的v-ptr来实现。这不是唯一可能的实现,并且已经证明其他方法可能会更快,但是它既简单又具有可预测的开销(在内存和调度速度方面)。

    如果我们使用一个简单的基类对象,并使用一个虚方法:
    struct B { virtual foo(); };

    对于计算机,没有成员方法之类的东西,因此实际上您具有:
    struct B { VTable* vptr; };

    void Bfoo(B* b);

    struct BVTable { RTTI* rtti; void (*foo)(B*); };

    当您从 B派生时:
    struct D: B { virtual foo(); virtual bar(); };

    现在,您有两种虚拟方法,一种覆盖 B::foo,另一种是全新的。计算机表示类似于:
    struct D { VTable* vptr; }; // single table, even for two methods

    void Dfoo(D* d); void Dbar(D* d);

    struct DVTable { RTTI* rtti; void (*foo)(D*); void (*foo)(B*); };

    注意 BVTableDVTable有何相似之处(因为我们将 foo放在 bar之前)?这一点很重要!
    D* d = /**/;
    B* b = d; // noop, no needfor arithmetic

    b->foo();

    让我们用某种机器语言将调用转换为 foo:
    // 1. get the vptr
    void* vptr = b; // noop, it's stored at the first byte of B

    // 2. get the pointer to foo function
    void (*foo)(B*) = vptr[1]; // 0 is for RTTI

    // 3. apply foo
    (*foo)(b);

    这些ptr由对象的构造函数初始化,在执行 D的构造函数时,会发生以下情况:
  • D::D()首先调用B::B(),以初始化其子部分
  • B::B()初始化vptr指向其vtable,然后返回
  • D::D()初始化vptr指向其vtable,覆盖B的

  • 因此,此处的 vptr指向D的vtable,因此应用的 foo是D的。对于 B,它是完全透明的。

    在这里B和D共享相同的vptr!

    4.多继承中的虚拟表

    不幸的是,这种共享并非总是可能的。

    首先,正如我们所看到的,在虚拟继承的情况下,“共享”项在最终完整对象中的位置很奇怪。因此,它具有自己的vptr。那是 1

    其次,在多重继承的情况下,第一个碱基与完整对象对齐,但是第二个碱基不能对齐(它们都需要空间来存储其数据),因此无法共享其vptr。那是 2

    第三,第一个基础与完整对象对齐,从而为我们提供了与简单继承(相同的优化机会)相同的布局。那是 3

    很简单,不是吗?

    关于c++ - 了解多继承的vptr?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5684574/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com