Monday, December 5, 2011

Virtual table in C++

The main purpose of virtual function mechanism is to implement polymorphism, which is one of the most important characters of OOP. Polymorphism, from some point of view, is to enable the base class to call the member function which is concrete, or override, in a derivative class. In another word, it tries to act different action without changing the code.

How to use virtual function is trivial to a coder. So I wont say much here. You may refer to some books about C++ for this topic. In this article, I would mainly focus on virtual table and analyze how virtual table works.

Virtual table, V-Table for short, is a table located in the top of an object. It is a list of addresses of virtual functions. Once we have such a table, inheritance, override could be implemented on top of it. For instance, if we have a pointer to parent class and point it to a derivative object, the virtual table contains the addresses of the function should be involved.

Let us check the following example:

typedef void(*Fun)(void);
 
class Base
{
public:
    virtual void a()
    {
        cout<<"Base::a()"<<endl;
    }
    virtual void b()
    {
        cout<<"Base::b()"<<endl;
    }
    virtual void c()
    {
        cout<<"Base::c()"<<endl;
    }
};
 
int main()
{
    Fun pFun = NULL;
    Base b;
    //
    cout<<"The address of virtual table: "<<(int *)*(int*)(&b)<<endl;
    cout<<"The address of the first virtual function: "<<(int*)*(int *)*(int*)(&b)<<endl;
    //
    pFun = (Fun)*(int *)*(int*)(&b);
    pFun();
    return 0;
}



The output of above code is as followed:


vt1


Furthermore, we could easily guess the addresses of other member functions as followed:


(Fun)*((int*)*(int*)(&b)+0); // Base::a()
(Fun)*((int*)*(int*)(&b)+1); // Base::b()
(Fun)*((int*)*(int*)(&b)+2); // Base::c()



Ok, the above addresses are too simple, right. Lets try an example a little complicated, simple inheritance without override.


Suppose we have two classes as the UML described.


o_Drawing3


As the derivative class does not override the functions in parent class, the virtual table are as followed:


o_vtable2


It is easy to find out that



  1. virtual functions are in the order of declaration;
  2. Functions in parent class are in front of derivative class.

For the case of inheritance with override, as the following classes.


o_Drawing4


The virtual table is as followed. Be aware of the first element in the table, which is different from the above example.


o_vtable3


From the figure above, we should notice that:



  1. the virtual function in parent class is replaced by the override function from the derivative class;

  2. the other functions remain the same.

So we run some code like:

Base* b = new Derive();
b->a();




We would get some result like “Derive::a()”.


Then, lets check the multiple inheritance. The first example is as followed:


o_Drawing1


As the first example, there is not virtual function override. So the virtual table would be very similar to simple inheritance. The only difference is that there would be as many virtual table as parent classes. In this case, there would be three virtual table, in the order of declaration.


o_vtable4



The final case is the multiple inheritance with overriding.


The classes are as follow:


o_Drawing2


The virtual table is as followed:


o_vtable5


We could find that all the virtual functions in the parent classes are replaced by the derivative class.



So, let try the following code to verify the above knowledge.


class Base1
{
public:
    virtual void a()
    {
        cout<<"Base1::a()"<<endl;
    }
    virtual void b()
    {
        cout<<"Base1::b()"<<endl;
    }
    virtual void c()
    {
        cout<<"Base1::c()"<<endl;
    }
};
class Base2
{
public:
    virtual void a()
    {
        cout<<"Base2::a()"<<endl;
    }
    virtual void b()
    {
        cout<<"Base2::b()"<<endl;
    }
    virtual void c()
    {
        cout<<"Base2::c()"<<endl;
    }
};
class Base3
{
public:
    virtual void a()
    {
        cout<<"Base3::a()"<<endl;
    }
    virtual void b()
    {
        cout<<"Base3::b()"<<endl;
    }
    virtual void c()
    {
        cout<<"Base3::c()"<<endl;
    }
};
 
class Derive: public Base1, public Base2, public Base3
{
public:
    void a()
    {
        cout<<"Derive::a()"<<endl;
    }
};
 
int main()
{
    Derive d;
    Base1 *b1 = &d;
    Base2 *b2 = &d;
    Base3 *b3 = &d;
    b1->a();
    b2->a();
    b3->a();
    b1->b();
    b2->b();
    b3->b();
    return 0;
}

vt2


Criticizes:


The virtual table mechanism plays an very important role in C++, without doubt. However, there are also some problems caused by it. 



  1. Try to access the functions in derivative class through a pointer of parent class. As shown in the following image:

o_vtable2


There is an entry for f1(), but I cannot access that function. However, you may violate this C++ grammar by directly decompose the virtual table to call it.



  1. Access non-public virtual function. Try the following code:


class Base 
{    
    private:virtual void f() 
    { 
        cout << "Base::f" << endl; 
    }
};
class Derive : public Base
{};
typedef void(*Fun)(void);
void main() 
{    
    Derive d;   
    Fun pFun = (Fun)*((int*)*(int*)(&d)+0);    
    pFun();
}





 


Thanks to the post here in Chinese. Some images in this blogs are from there.


No comments:

Post a Comment