0
class Base1 { 
    virtual void fun1() { cout << "Base1::fun1()" << endl; } 
    virtual void func1() { cout << "Base1::func1()" << endl; } 
}; 
class Base2 { 
    virtual void fun1() { cout << "Base2::fun1()" << endl; } 
    virtual void func1() { cout << "Base2::func1()" << endl; } 
}; 

class Test:public Base1,public Base2
{
public:
    virtual void test(){cout<<"Test";}
};


typedef void(*Fun)(void); 

int main()
{
    Test objTest; 
    Fun pFun = NULL;

    pFun = (Fun)*((int*)*(int*)((int*)&objTest+0)+0); pFun(); 
    pFun = (Fun)*((int*)*(int*)((int*)&objTest+0)+1); pFun(); 

//The following isnt supposed to print Test::test() right?
    pFun = (Fun)*((int*)*(int*)((int*)&objTest+0)+2); pFun(); 

    pFun = (Fun)*((int*)*(int*)((int*)&objTest+1)+0); pFun(); 
    pFun = (Fun)*((int*)*(int*)((int*)&objTest+1)+1); pFun();


//Isnt the following supposed to print Test:test() because the order of   
construction   object is Base1 followed by construction of Base2 followed by 
construction of Test.

    pFun = (Fun)*((int*)*(int*)((int*)&objTest+1)+2); pFun(); 
}

The sizeof Test object is 8 bytes. So it is evident from this example that the object consists of two 4 byte _vptr's. Since the order of inheritance is public Base1,public Base2 that means the object should be made in the following way:

| _vptr to class Base1 vTable | -->this Base1 vtable should have 2 elements.
| _vptr to class Base2 vTable | -->this Base2 vtable should have 3 elements.

But from the code snippet it looks like the object is made as:

| _vptr to class Base1 vTable | -->this Base1 vtable should have 3 elements.
| _vptr to class Base2 vTable | -->this Base2 vtable should have 2 elements.

The first vptr points an array of 3 function pointers(1st points Base1::fun1(), 2nd points to Base1::func1() and third points to Test::test() ).

A derived object is made up of Base+Derived. That means first chunk of bytes is Base object and the remaining is Derived. If so, then in our example of objTest , the second _vptr should be supposed to point to three function pointers (1st to Base2::fun1(), Base2::func1() and Test::test() ). But we see instead that the first _vptr points to function pointer of Test::test().

Question:

1. Is this behavior compiler specific ?

2. Does the standard mention anything about this behavior? Or my understanding is wrong completely?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
anurag86
  • 1,635
  • 1
  • 16
  • 31
  • All of this is undefined behaviour , you use an `int` expression to read an object that was not declared as an `int` – M.M Mar 10 '16 at 05:36
  • I didnt understand you. I am typecasting to int (meaning i am trying to read those bytes which make up the object as integer). Whats wrong in that? – anurag86 Mar 10 '16 at 09:18
  • That isn't allowed because of something called the *strict aliasing rule*. To try and inspect arbitrary memory you need to use `char` or preferably `unsigned char`. – M.M Mar 10 '16 at 12:43

2 Answers2

0

How the vtable is constructed is definitely compiler dependent. As is what happens when you have multiple inheritance.In particular what order the two vtables are produced...

There are also OTHER things stored, beyond the vtable - such as type information so dynamic casting can be done.

All that is required from the standard is that "virtual functions work" (sure, it's thousands of words describing what "work" means), but how it's implemented is entirely up to the compiler [and C++ library to some degree perhaps].

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
0

First, please note that vtables are outside the scope of the C++ standard because the standard is only concerned with the behavior of conforming programs, not how the implementation accomplish that.

All known implementations use vtables to implement virtual functions and RTTI, but significant nuances exist in more "subtle" features like multiple inheritance, covariant returns, and virtual bases.

In the simpler case the vtable of the base class can be extended because the base class subobject lies at the same address as the derived object; the base class is called a "primary base".

In a more complex case, the derived class has multiple polymorphic base classes, each with its own vptr. One base class will be the primary base, and the derived class will extend its vtable. The layout of the object is such that other base class subobjects will be at a non zero offset in the derived object, so the this pointer will require an adjustment when a virtual function is called through the vtable of such non-primary base; the functions referenced in that vtable expect a pointer to a base class subobject or complete object:

  • the functions referenced in the vtable of the base class expect a pointer to a complete base object;
  • the functions referenced in the non primary base vtable of the derived object expect a pointer to a base subobject; in order to obtain a correct this pointer, they need to do a base to derived adjustment.

The calls to virtual function through a non primary base are slightly less efficient because of the adjustment needed. A compiler will prefer the use of the primary vptr.

The compiler will extend the vtable of the primary base with all the virtual functions inherited from other base classes.

curiousguy
  • 8,038
  • 2
  • 40
  • 58