We all know that when using simple single inheritance, the address of a derived class is the same as the address of the base class. Multiple inheritance makes that untrue.
Does virtual inheritance also make that untrue? In other words, is the following code correct:
struct A {};
struct B : virtual A
{
int i;
};
int main()
{
A* a = new B; // implicit upcast
B* b = reinterpret_cast<B*>(a); // fishy?
b->i = 0;
return 0;
}
Definitions: A class that is derived from another class is called a subclass (also a derived class, extended class, or child class). The class from which the subclass is derived is called a superclass (also a base class or a parent class).
Derived class pointer cannot point to base class.
Virtual base class in C++ Virtual classes are primarily used during multiple inheritance. To avoid, multiple instances of the same class being taken to the same class which later causes ambiguity, virtual classes are used.
Sub Class: The class that inherits properties from another class is called Sub class or Derived Class. Super Class:The class whose properties are inherited by sub class is called Base Class or Super class.
We all know that when using simple single inheritance, the address of a derived class is the same as the address of the base class.
I think the claim is not true. In the below code, we have a simple (not virtual) single (non multiple) inheritance, but the addresses are different.
class A
{
public:
int getX()
{
return 0;
}
};
class B : public A
{
public:
virtual int getY()
{
return 0;
}
};
int main()
{
B b;
B* pB = &b;
A* pA = static_cast<A*>(pB);
std::cout << "The address of pA is: " << pA << std::endl;
std::cout << "The address of pB is: " << pB << std::endl;
return 0;
}
and the output for VS2015 is:
The address of pA is: 006FF8F0
The address of pB is: 006FF8EC
Does virtual inheritance also make that untrue?
If you change the inheritance in the above code into virtual, the result will be the same. so, even in the case of virtual inheritance, the addresses of base and derived objects can be different.
The result of reinterpret_cast<B*>(a);
is only guaranteed to point to the enclosing B
object of a
if the a
subobject and the enclosing B
object are pointer-interconvertible, see [expr.static.cast]/3 of the C++17 standard.
The derived class object is pointer-interconvertible with the base class object only if the derived object is standard-layout, does not have direct non-static data members and the base class object is its first base class subobject. [basic.compound]/4.3
Having a virtual
base class disqualifies a class from being standard-layout. [class]/7.2.
Therefore, because B
has a virtual base class and a non-static data member, b
will not point to the enclosing B
object, but instead b
's pointer value will remain unchanged from a
's.
Accessing the i
member as if it was pointing to the B
object then has undefined behavior.
Any other guarantees would come from your specific ABI or other specification.
Multiple inheritance makes that untrue.
That is not entirely correct. Consider this example:
struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};
When creating an instance of D
, B
and C
are instantiated each with their respective instance of A
. However, there would be no problem if the instance of D
had the same address of its instance of B
and its respective instance of A
. Although not required, this is exactly what happens when compiling with clang 11
and gcc 10
:
D: 0x7fffe08b4758 // address of instance of D
B: 0x7fffe08b4758 and A: 0x7fffe08b4758 // same address for B and A
C: 0x7fffe08b4760 and A: 0x7fffe08b4760 // other address for C and A
Does virtual inheritance also make that untrue
Let's consider a modified version of the above example:
struct A {};
struct B : virtual A {};
struct C : virtual A {};
struct D : B, C {};
Using the virtual
function specifier is typically used to avoid ambiguous function calls. Therefore, when using virtual
inheritance, both B
and C
instances must create a common A
instance. When instantiating D
, we get the following addresses:
D: 0x7ffc164eefd0
B: 0x7ffc164eefd0 and A: 0x7ffc164eefd0 // again, address of A and B = address of D
C: 0x7ffc164eefd8 and A: 0x7ffc164eefd0 // A has the same address as before (common instance)
Is the following code correct
There is no reason here to use reinterpret_cast
, even more, it results in undefined behavior. Use static_cast
instead:
A* pA = static_cast<A*>(pB);
Both casts behave differently in this example. The reinterpret_cast
will reinterpret pB
as a pointer to A
, but the pointer pA
may point to a different address, as in the above example (C vs A). The pointer will be upcasted correctly if you use static_cast
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With