Low-level information about inheritance and polymorphism

This question is one of the big doubts that arise around my head, and it is also difficult to describe it in terms of words. This seems obvious several times, and sometimes difficult to crack. So the question goes like this:

  class Base {
 public:
      int a_number;

      Base () {}
      virtual void function1 () {}
      virtual void function2 () {}
      void function3 () {}
 };

 class Derived: public Base {
 public:
      Derived (): Base () {}
      void function1 () {cout & lt & lt "Derived from Base" & lt & lt endl; 
      virtual void function4 () {cout & lt & lt "Only in derived" & lt & lt endl;}
 };

 int main () {

       Derived * der_ptr = new Derived ();
       Base * b_ptr = der_ptr;  // As just address is being passed, b_ptr points to derived object

       b_ptr -> function4 ();  // Will Give Compilation ERROR !!

       b_ptr -> function1 ();  // Calls the Derived class overridden method

       return 0;

 }

Q1. Although b_ptr points to a Derived object to which VTABLE it accesses and HOW? since b_ptr -> function4 () gives a compilation error. Or is it that b_ptr can only access that size of the VTABLE base class in Derived VTABLE?

Q2. Since the Derived memory layout must be (Base, Derived), is the Base class VTABLE also included in the Derived class memory layout?

Q3. Since function 1 and function2 of the base class Vtable points to the implementation of the class Base and function2 of the class Derived to function2 of the base class, is VTABLE really needed in the base class? (This may be the dumbest question I have ever asked, but still I doubt it in my current state, and the answer should be related to the answer of Q1 :))

Please comment.

Thank you for your patience.

+6
c ++ inheritance polymorphism vtable
source share
6 answers

As an additional illustration, here is the C version of your C ++ program, showing vtables and all.

#include <stdlib.h> #include <stdio.h> typedef struct Base Base; struct Base_vtable_layout{ void (*function1)(Base*); void (*function2)(Base*); }; struct Base{ struct Base_vtable_layout* vtable_ptr; int a_number; }; void Base_function1(Base* this){} void Base_function2(Base* this){} void Base_function3(Base* this){} struct Base_vtable_layout Base_vtable = { &Base_function1, &Base_function2 }; void Base_Base(Base* this){ this->vtable_ptr = &Base_vtable; }; Base* new_Base(){ Base *res = (Base*)malloc(sizeof(Base)); Base_Base(res); return res; } typedef struct Derived Derived; struct Derived_vtable_layout{ struct Base_vtable_layout base; void (*function4)(Derived*); }; struct Derived{ struct Base base; }; void Derived_function1(Base* _this){ Derived *this = (Derived*)_this; printf("Derived from Base\n"); } void Derived_function4(Derived* this){ printf("Only in derived\n"); } struct Derived_vtable_layout Derived_vtable = { { &Derived_function1, &Base_function2}, &Derived_function4 }; void Derived_Derived(Derived* this) { Base_Base((Base*)this); this->base.vtable_ptr = (struct Base_vtable_layout*)&Derived_vtable; } Derived* new_Derived(){ Derived *res = (Derived*)malloc(sizeof(Derived)); Derived_Derived(res); return res; } int main(){ Derived *der_ptr = new_Derived(); Base *b_ptr = &der_ptr->base; /* b_ptr->vtable_ptr->function4(b_ptr); Will Give Compilation ERROR!! */ b_ptr->vtable_ptr->function1(b_ptr); return 0; } 
+7
source share

Q1 - name resolution is static. Since b_ptr is of type Base *, the compiler cannot see any of the names unique to Derived to access their entries in v_table.

Q2 - Maybe, maybe not. You should remember that vtable itself is just a very common method for implementing polymorphism at runtime and is not really part of the standard anywhere. No final statement can be made about where he is. Vtable really can be some kind of static table somewhere in the program that the pointer points to inside the description of the instance object.

Q3. If a virtual record in one place should be in all places, otherwise a bunch of difficult / impossible checks will be required to provide the possibility of redefinition. If the compiler KNOWS that you have a base and an overridden function is called, but access to vtable is not required, but can simply use the function directly; he can even embed it if he wants.

+3
source share

A1. The vtable pointer points to Derived vtable, but the compiler does not know this. You said that you viewed it as a base pointer, so it can only call methods that are valid for the Base class, regardless of what the pointer points to.

A2. The vtable format is not specified by the standard, it is not even officially part of the class. This is just 99.99% of the most common implementation method. Vtable is not part of the layout of the object, but there is a pointer to the vtable, which is a hidden element of the object. It will always be at the same relative location in the object so that the compiler can always generate code to access it, regardless of what class pointer it is. Everything becomes more complicated with multiple inheritance, but it still does not go there.

A3. Vtables exist once in a class, and not once for each object. The compiler must generate one, even if it is never used, because it does not know what is ahead of time.

+3
source share

b_ptr points to Derived vtable, but the compiler cannot guarantee that the derived class has function_4 in it, because it is not contained in the vtable base table, so the compiler does not know how to make the call and throws an error.

No, vtable is a static constant elsewhere in the program. The base class simply contains a pointer to it. The Derived class may contain two vtable pointers, but it may not be.

In the context of these two classes, Base needs a virtual table to find Derived function1, which is actually virtual, even if you did not mark it as such, because it redefined the virtual function of the base class. However, even if this was not the case, I am sure that the compiler should create vtables anyway, since it does not know what other classes you have in other translation units that may or may not inherit from these classes and override them virtual functions in unrecognizable ways.

+1
source share

First and foremost, remember that C ++ does not do much runtime introspection. Basically, he should know everything about objects at compile time.

Q1 - b_ptr - pointer to the base. Therefore, he can only access things that are present in the base object. With no exceptions. Now the actual implementation may change depending on the actual type of the object, but there is no need to bypass the method that must be defined in Base if you want to call it with the Base pointer.

Q2. The simple answer is “yes, the vtable for the database must be present in the derivative,” but there are many possible strategies for building the vtable, so don't be obsessed with the exact structure.

Q3 - Yes, there must be a vtable in the base class. Everything that calls virtual functions in the class will go through vtable, so if the underlying object is actually derived, everything can work.

Now that is not absolute, because if the compiler can be absolutely sure that he knows what he got (as it could be in the case of a base object declared on the local stack), then the compiler is allowed to optimize vtable, and may even be allowed to embed a function.

+1
source share

It all depends on the implementation. But here are the answers to the simplest simple way using "vtables".

The Base class has a vtable pointer, so the base view is something like this pseudocode:

 struct Base { void** __vtable; int a_number; }; void* __Base_vtable[] = { &Base::function1, &Base::function2 }; void __Base_ctor( struct Base* this_ptr ) { this_ptr->__vtable = __Base_vtable; } 

The Derived class includes a subobject of the Base class. Since this is the place for vtable, Derived does not need to add another one.

 struct Derived { struct Base __base; }; void* __Derived_vtable[] = { &Derived::function1, &Base::function2, &Derived::function4 }; void __Derived_ctor( struct Derived* this_ptr ) { __Base_ctor( &this_ptr->__base ); this_ptr->__base.__vtable = __Derived_vtable; } 

"vtable for the base class", __Base_vtable in my pseudo-code, is necessary if someone tries new Base(); or Base obj; .

All of the above is complicated when using multiple inheritance or virtual inheritance ....

For the string b_ptr -> function4(); this is a compile-time error not related to vtables. When you add a Base* pointer, you can only use this pointer in the methods defined by class Base (since the compiler no longer “knows” whether it is Derived , a Base or another class). If Derived has its own data item, you cannot access it through this pointer. If Derived has its own member function, virtual or not, you cannot access it through this pointer.

+1
source share

All Articles