Pointer casting offset of a class with single inheritance

Viewed 156

While learning the "Effective C++", I was firstly surprised when I learned the fact that if a class had multiple inheritance, its pointer may take offset when the pointer casting is done. Although it was not easy concept to grasp, but I think I managed to get it.

However, the author claims that this offset might happen even in the pointer casting of singly inherited class. I wonder what would be the such case, and wish to know the rationale behind it.

2 Answers

This can happen when polymorphism is introduced into a class hierarchy by a derived class. Consider the following class:

struct Foo
{
    int a;
    int b;
};

This class is not polymorphic, and thus the implementation does not need to include a pointer to a virtual dispatch table (a commonly-used method of implementing virtual dispatch). It will be laid out in memory like this:

  Foo
  +---+
a |   |
  +---+
b |   |
  +---+

Now consider a class that inherits from Foo:

struct Bar : Foo
{
    virtual ~Bar() = default;
};

This class is polymorphic, and so objects of this class need to include a pointer to a vtable so further derived classes can override Bar's virtual member functions. That means that Bar objects will be laid out in memory like this:

               Bar
               +---------+
vtable pointer |         |
               +---------+
 Foo subobject |   +---+ |
               | a |   | |
               |   +---+ |
               | b |   | |
               |   +---+ |
               +---------+

Since the object's Foo subobject is not at the beginning of the object, any Foo* initialized from a pointer to a Bar object will need to be adjusted by the size of a pointer so that it actually points at the Bar object's Foo subobject.

Live Demo

class B {
  int a = 0;
};

class D : public B {
  virtual ~D() = default;
};

D has a virtual member. B does not. A common implementation of dynamic dispatch in C++ involves keeping a hidden pointer to a table of function addresses at the begining of the object.

This means the first byte of the B sub-object won't be at the start of the complete D object. A pointer cast would need to adjust the address by the vptr size.

Related