Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subclass address equal to virtual base class address?

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;
}
like image 893
user1610015 Avatar asked Feb 13 '20 07:02

user1610015


People also ask

What is the difference between base class and subclass?

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).

Can a derived class pointer point to a base class object?

Derived class pointer cannot point to base class.

What is virtual base class in inheritance?

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.

What is a subclass in C++?

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.


3 Answers

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.

like image 117
Gupta Avatar answered Oct 16 '22 20:10

Gupta


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.

like image 24
walnut Avatar answered Oct 16 '22 21:10

walnut


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.

like image 2
mfnx Avatar answered Oct 16 '22 22:10

mfnx