C++中,为什么指针转换时地址变了?

In C++, why is the address changed when the pointer is converted?

代码如下:

#include <iostream>
using namespace std;

class B1 {
public:

  virtual void f1() {
      cout << "B1\n";
      }
};

class B2 {
public:

  virtual void f1() {
      cout << "B2\n";
      }
};        

class D : public B1, public B2 {
public:
  void f1() {
      cout << "OK\n" ;
  }
};

int main () {

D dd;    
B1 *b1d = &dd;
B2 *b2d = &dd;
D *ddd = &dd;

cout << b1d << endl;
cout << b2d << endl;
cout << ddd << endl;

b1d -> f1();
b2d -> f1();
ddd -> f1();
}

输出为:

0x79ffdf842ee0
0x79ffdf842ee8
0x79ffdf842ee0
OK
OK
OK

这让我感到困惑,因为我预计 b1db2d 会相同,因为它们都指向 dd。但是,b1db2d 的值根据结果不同。我认为这可能与类型转换有关,但我不确定它是如何工作的。

有人对此有想法吗?

D 继承自 B1B2.

由于 B1 是从第一个对象继承的,所以首先要构造对象的 B1 部分,然后再创建对象的 B2 部分,然后 D.

因此,当您将派生类型的指针转​​换为基类型时,您看到的是这些部分在内存中的不同之处。

b1dddd 具有相同的地址,因为它们都指向内存中 class 的开头。

b2d 是偏移量,因为它指向 D.

B2 部分的开始

C++标准规定对象的大小必须为at least 1(字节)。两个独立的对象不能有相同的地址.

子对象可以与包含它的对象具有相同的地址。通常,任何子对象都不能与另一个子对象具有相同的地址,因为它们没有直接关系。因此(通常)至多一个子对象可以具有与容器对象相同的地址。

在这种情况下,D 的实例包含 2 个子对象。它们都是基础 class 子对象。其中一个与容器对象具有相同的地址,而另一个则没有。

当您将派生类型的指针转​​换为基类型时,转换后的指针将指向基 class 子对象。具有不同地址的子对象并不奇怪。与容器具有相同地址的子对象之一也不足为奇。

上面一段的规则其实有一个例外。空基础 class 子对象不需要任何大小。这被称为 empty base optimization。不过,您的基础 class 并不为空,因此不适用。

您的看法部分正确。这个指针指的是对象的地址,它当然是 class 的一部分。更正式地说,这是指向 class 的 vtable 的指针。但是如果你从多个 classes 继承。那么这个应该指向什么呢?

假设你有这个:

class concrete : public InterfaceA, public InterfaceB

从 interfaceA 和 interfaceB 继承的具体对象必须能够像 bot interfaceA 和 interfaceB 一样工作(这就是 public 的要点:当您继承时)。所以应该有一个 "this- adjustment" 这样才能做到这一点。

通常,在多重继承的情况下,选择基 class(例如 interfacea)。在那种情况下,

几乎每个编译器都有一个 "convention" 来生成代码。例如,为了调用 funa,编译器生成的程序集类似于:

call *(*objA+0)

其中+0部分是函数在vtable中的偏移量。

编译器需要在编译时知道此方法的 (funa) 偏移量。

现在如果你想调用 funb 会发生什么?根据我们所说的,funb 需要位于 interfaceB 对象的偏移量 0 处。所以有 thunk adjustor 来调整它,使其指向 interfaceB 的 vtable,这样 funB 就可以正确调用,再次使用:

call *(*objB+0)

如果你这样声明:

concrete *ac = new concrete();
interfaceB *ifb = ac;

你期待什么?具体现在扮演接口B的角色:

如果我没记错的话,你可以打印 ifb 和 ac(它们是指针),并验证它们指向不同的地址,但是如果你检查它们是否相等:

ifb == ac;

你应该是对的,因为它们被调整以描述它们是同一个动态生成的对象。