0

If we had this code:

class Base
{
int anint;
float afloat;
};
class Derived : Base
{
//inherited member variables...
};

I have been told that members of Base would be inherited to Derived and these the inherited members in Derived are actually inside a base class subobject of Base (but this subobject is unnamed); a subobject of Base is created in Derived that holds the members that are inherited. So when accessing a member in a class, there is an implicit invocation to the this pointer, unless you do something explicitly, but is there also an implicit pointer (or anything) invoked when accessing an inherited object? Like, if we accessed anint in an instance of Derived by derivedInstance->anint, would this actually look like derivedInstance->this->somethingToBaseObjectThatHoldsTheMembers->anint or how does this work?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • 1
    Compiler is able to figure out the correct offset of all the base classes members, so unless no virtual inheritance is in game it will be like `this + member offset ->`, or even completely optimized away if compiler manages to inline class and just store `anint` and `afloat` in registers. [Probably related](https://stackoverflow.com/questions/1658294/whats-the-purpose-of-the-lea-instruction). – user7860670 Sep 18 '17 at 13:02
  • 1
    Classes don't have a representation in compiled code (except possibly for a vtable pointer) apart for their data members, any more than structs do in C. –  Sep 18 '17 at 13:05
  • @VTT so what you are saying is that there is _not_ some implicit thing that is added when accessing members that are inherited and stored inside a base class subobject? –  Sep 18 '17 at 13:41
  • 1
    You can think of `derivedInstance->this->somethingToBaseObjectThatHoldsTheMembers->anint` chain as of steps required to calculate offset to member. But since each offset is known at compile time (unless there is no virtual inheritance) resulting offset will be calculated at compile time as well and no extra redirections occur. In case of virtual inheritance there will be an extra redirection through a pointer to base class. – user7860670 Sep 18 '17 at 13:50
  • @VTT okay, thanks! But there is nothing like the `this`pointer, which you _can_ write explicitly, for accessing members that memory-wise is inside the unnamed base class subobject, only a hypothetical one? –  Sep 18 '17 at 14:05

2 Answers2

1

There is no special pointer to base class data. The layout of data members in C++ follows closely from C. (In fact your example has no virtual methods so must follow C exactly). So lets consider how your code would look in C:

 struct Base
 {
    int anint;
    float afloat;
 };
 struct Derived
 {
   struct Base; // C style inherit from struct Base
   int a2ndInt;
 };

In C, the structure memory layout is defined to be pretty much as you write it. This means that the struct Derived has basically the following memory layout.

struct Derived
{
    int anint;
    float afloat;
    int a2ndInt;
};

The this pointer is to the start of the struct, so accessing anint or afloat from either a pointer to Derived or Base involves the same memory offset. Thus down casting is always easy here.

Things get more complicated when you have virtual functions as the data structure then has to have a hidden pointer to its virtual functions, but there need only be one such pointer. Lets consider the single inheritance case and you might imagine a layout something like (the actual layout depends on the ABI):

 struct Base
 {
    <ABI defined pointer type> * class_; // hidden virtual function table
    int anint;
    float afloat;
 };
 struct Derived
 {
   struct Base; // inherit from struct Base
   int a2ndInt;
 };

Now the struct Derived might have the following memory layout. Note that when creating a Derived object the constructor must set the class_ pointer. This is one reason why the constructors start from the Base class constructor, as each Derived class can then override the class_ pointer.

struct Derived
{
    <ABI defined pointer type> * class_; 
    int anint;
    float afloat;
    int a2ndInt;
};

So again accessing anint or afloat from either a pointer to Derived or Base involves the same offset. Thus down casting is again easy here.

Multiple inheritance is much more complicated and is where static_cast<> for down casting is essential. This case is closest to what you are thinking but still only involves an offset to a single this pointer.

struct Base1
{
   <ABI defined pointer type> * class_; // hidden virtual function table
   int anint;
};
struct Base2
{
   <ABI defined pointer type> * class_; // hidden virtual function table
   float afloat;
};

I am not so familiar with the ABI but I imagine the hidden virtual table pointers could be merged somehow resulting in a memory layout something like:

struct Derived
{
    <ABI defined pointer type> * class_; // merged Base1 and Base2
    int anint;
    float afloat;
    int a2ndInt;
};

or not merged (depending on the ABI)

struct Derived
{
    <ABI defined pointer type> * class_; // from Base1
    int anint;
    <ABI defined pointer type> * class_; // from Base2
    float afloat;
    int a2ndInt;
};

So again accessing anint from a pointer to Derived or Base1 involves the same offset, but accessing afloat does not work. This means that using a C-style cast (i.e. using (Base2*)) from a Derived pointer to a Base2 pointer fails, you need static_cast<> which handles the change in offset.

Note that this is not so complicated if only one of the base classes has member data. This is why it is often recommended that, when using multiple inheritance, only one of the base classes should have data.

NOTE: The real ABI defines the true layout of data in the structure. What is shown here is for illustration purposes only.

Justin Finnerty
  • 329
  • 1
  • 7
-1

No. The compiler choses a layout (ABI). Static casts leverage knowledge of that layout to adjust pointers using static_cast.

RTTI enables dynamic pointer adjustments using dynamic_cast.

See e.g. Regular cast vs. static_cast vs. dynamic_cast

sehe
  • 374,641
  • 47
  • 450
  • 633