In C++, why is the address changed when the pointe

2019-02-25 04:38发布

Following is the code:

#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();
}

The output is :

0x79ffdf842ee0
0x79ffdf842ee8
0x79ffdf842ee0
OK
OK
OK

This looks confusing to me, because I expected b1d and b2d would be the same as both of them point to dd. However, the value of b1d and b2d is different according to the result. I thought it may be related to type casting but I'm not sure how it works.

Does anyone have ideas about this?

3条回答
Summer. ? 凉城
2楼-- · 2019-02-25 05:17

The C++ standard specifies that the size of an object must be at least 1 (byte). Two separate objects can not have the same address.

A subobject can have the same address as the object that contains it. Typically, no subobject can have the same address as another subobject because they are not directly related. Therefore (typically) at most one subobject can have the same address as the container object.

In this case, an instance of D contains 2 subobjects. They are both base class subobjects. One of them has the same address as the container object and the other does not.

When you cast a pointer of derived type to the base type, the casted pointer will point to the base class subobject. There is nothing surprising about the subobjects having a different address. Nor is there anything surprising about one of the subobjects having the same address as the container.

There is actually an exception to the rules in the top paragraph. Empty base class subobjects do not need to have any size. This is known as empty base optimization. Your base classes are not empty though, so that does not apply.

查看更多
贪生不怕死
3楼-- · 2019-02-25 05:24

D inherits from B1 andB2.

Since B1 is inherited from first the B1 part of the object is going to be constructed first and then the B2 part of the object will be created then D.

So what you are seeing is the difference of where those parts are in memory when you cast a pointer of the derived type to the base type.

b1d and ffffd have the same address as they both point to the beginning of the class in memory.

b2d is offset as it points to the start of the B2 part of D.

查看更多
看我几分像从前
4楼-- · 2019-02-25 05:27

Your perception of this is partially true. This pointer refers to the address of the object which of course is part of a class. To be more formal this is a pointer to the vtable of that class. But in the case you inherit from multiple classes. So what should this point to?

Say you have this:

class concrete : public InterfaceA, public InterfaceB

A concrete object, that inherits from both interfaceA and interfaceB must be able to act as if it was bot interfaceA and interfaceB (that's the point of the public: when you inherit). So there should be a "this- adjustment" so that this can be done.

Normally, in case of multiple inheritance, a base class is selected (say for example interfacea). In that case,

object layout

Pretty much every compiler has a "convention" to produce code. In order to call funa for example, the compiler's produced assembly is something like:

call *(*objA+0)

Where the +0 part is the offset of the function inside the vtable.

Compiler needs to know this method's (funa) offset at compile time.

Now what happen if you want to call funb? Based on what we 've said, funb needs to be at offset 0 of an interfaceB object. So there is thunk adjustor to adjust this so that it points to interfaceB's vtable, so that funB can properly be called, again with:

call *(*objB+0)

If you declare something like this:

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

what do you expect? concrete now plays the role of interfaceB:

If I remember correctly, you can print ifb and ac (they are pointers), and verify that they point to different addresses, but if you check them for equality:

ifb == ac;

You should be getting true, cause they are adjusted in order to depict that they are the same dynamically generated object.

object layout

查看更多
登录 后发表回答