【问题标题】:How dose base pointer address in derived class?派生类中的基指针地址如何?
【发布时间】:2020-05-02 11:53:43
【问题描述】:

据我所知,我们可以使用基类指针来寻址和调用派生类中的虚函数,因为基类指针的指针范围更有限。但是我只想知道,基类指针是怎么知道派生类从哪里开始的?

例如,对于记录A、B、C都有自己的数据成员,我们可以分两种不同的类别来讨论这个问题:1.A、B、C都有自己的虚函数; 2. A、B、C只有自己的数据成员,没有任何虚函数

class A {...};
class B {...};
class C : public A, public B {...};

C c;
B* b = &c

在C内部,A应该放在B的顶部,那么指针b如何知道在C中从哪里开始寻址?

【问题讨论】:

  • 因为编译器知道A的大小。如果你输出这两个指针你会看到指针的值已经改变了。试试cout << (void*)&c << ' ' << (void*)b << '\n';
  • @john 地址不会改变。他们应该打印同样的东西。编辑:哦,如果他们有虚拟方法,你的意思是?是的,那么它们会有所不同。
  • 它没有。基类定义指定了它有哪些虚函数。当指向的对象是派生类的实例时,实现(也称为编译器)确保所有虚函数调用都解析为最派生类的覆盖。实际上,实现通常使用包含(以某种形式)指向所有虚函数的指针的虚函数表(vtables)。在构造派生类的实例时,基类的 vtable 会填充派生类的条目(因为“派生”部分的构造发生在“基”部分之后)
  • @TedLyngmo static_cast<A*>(&c)static_cast<B*>(&c) 中的至少一个必须与&c 不同,因为AB 子对象必须具有不同的地址。
  • @molbdnilo 是的,如果他们有虚拟方法的话。如果他们没有,他们将有相同的地址。

标签: c++ pointers inheritance memory


【解决方案1】:

编译器知道布局,所以它会发出代码,计算CB子对象的地址。

让我们看一个简单的实际例子:

class A { int x; };
class B { int y; };
class C: public A, public B { };

B *get(C &c) {
    return &c;
}

get 中,编译器必须计算BC 中的位置。以下是编译后代码的示例(godbolt):

get(C&):                              # @get(C&)
        lea     rax, [rdi + 4]
        ret

这意味着编译器会将 4 个字节添加到输入参数 (c) 的地址,并返回(它添加 4,因为 sizeof(A) 是 4,并且编译器决定不添加任何额外的填充)。

注意,如果A 为空,那么B 的地址很可能与C 相同。如果A 不为空(具有非静态数据成员,或具有虚函数),那么B 的地址可能会有所不同。但所有这些都是实现细节,取决于平台 ABI(应用程序二进制接口)。

【讨论】:

  • 最后一段是否受到EBO的影响?
  • @Evg: 是的,如果编译器将A 放在首位(并且A 为空),那么EBO 需要将BC 放在同一个地址。跨度>
猜你喜欢
  • 2020-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
相关资源
最近更新 更多