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

Han*_*Sun 3 c++ oop pointers virtual-functions multiple-inheritance

以下是代码:

#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();
}
Run Code Online (Sandbox Code Playgroud)

输出是:

0x79ffdf842ee0
0x79ffdf842ee8
0x79ffdf842ee0
OK
OK
OK
Run Code Online (Sandbox Code Playgroud)

这看起来让我感到困惑,因为我期望b1d并且b2d两者都指向相同dd.但是,根据结果,b1d和的值b2d是不同的.我认为这可能与打字有关,但我不确定它是如何工作的.

有没有人有这个想法?

Nat*_*ica 12

D继承自B1B2.

由于B1从头开始继承,因此首先B1构造B2对象的一部分,然后创建对象的一部分D.

所以你看到的是当你将派生类型的指针强制转换为基类型时,这些部分在内存中的位置的差异.

b1d并且ddd具有相同的地址,因为它们都指向内存中类的开头.

b2d是偏移的,因为它指向B2部分的开头D.


ses*_*tus 5

您对此的看法部分正确.该指针指的是对象的地址,当然这是一个类的一部分.为了更正式,这是指向该类的vtable的指针.但是在你从多个类继承的情况下.那么这应该指向什么呢?

说你有这个:

class concrete : public InterfaceA, public InterfaceB
Run Code Online (Sandbox Code Playgroud)

从interfaceA和interfaceB继承的具体对象必须能够像bot interfaceA和interfaceB一样行动(这是公共的重点:当你继承时).所以应该有一个"这个调整",这样就可以做到.

通常,在多重继承的情况下,选择基类(例如,接口).在这种情况下,

对象布局

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

call *(*objA+0)
Run Code Online (Sandbox Code Playgroud)

其中+0部分是vtable内函数的偏移量.

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

现在,如果你想打电话,会发生什么?基于我们所说的,funb需要位于interfaceB对象的偏移0处.所以有一个thunk adjustor来调整它,使它指向interfaceB的vtable,这样就可以正常调用funB,同样:

call *(*objB+0)
Run Code Online (Sandbox Code Playgroud)

如果你声明这样的东西:

concrete *ac = new concrete();
interfaceB *ifb = ac;
Run Code Online (Sandbox Code Playgroud)

你能指望什么?具体现在扮演interfaceB的角色:

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

ifb == ac;
Run Code Online (Sandbox Code Playgroud)

你应该变得正确,因为它们被调整以便描述它们是相同的动态生成的对象.

对象布局