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 = ⅆ
B2 *b2d = ⅆ
D *ddd = ⅆ
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?
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.
D
inherits fromB1
andB2
.Since
B1
is inherited from first theB1
part of the object is going to be constructed first and then theB2
part of the object will be created thenD
.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
andffffd
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 theB2
part ofD
.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:
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,
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:
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:
If you declare something like this:
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:
You should be getting true, cause they are adjusted in order to depict that they are the same dynamically generated object.