Pages

Thursday, July 29, 2010

C++ polymorphism

This code is to explain how polymorphism works through virtual functions and how C++ does it using VPTR and VTABLE. Please refer to my last post - C++ test code on virtual functions – to understand this better.

Code example to find out VPTR and VTABLE in C++ class. And use it to show how
polymorphism works in C++. That is same base class pointer can be used to
invoke functions of the derived class. C++ achieves this by changing the VPTR
pointer to new drived classes VTABLE.

VPTR and VTABLE usage for polymorphism in C++ Class expalined diagrammatically below. The base class object VPTR changes when base class is instantiated with that sub class, to point to the VTABLE of that sub class.

'?' - denotes VPTR will decide what subclass is currently instantiated to
the base class.

BaseClass Object
    (pobj)               SubClass_1
       |               VTABLE (vtable)
       v                     |
+------------+               v
| VPTR (vptr)|     *-->+------------+
+------------+  ?      |  VTEntry1  |------------> +------------+
| DATA1 (a)  |     *   +------------+              |  func1()   |
+------------+     |   |  VTEntry2  |--------+     |            |
| DATA2 (b)  |     |   +------------+        |     |            |
+------------+     |   |      .     |        |     +------------+
|      .     |     |   |      .     |        |
|      .     |     |   |      .     |        +---->+------------+
|      .     |     |                               |   func2()  |
|      .     |     |                               |            |
                   |                               |            |
                   |                               +------------+
                   |     SubClass_2
                   |   VTABLE (vtable)
                   |         |
                   |         v
                   +-->+------------+
                       |  VTEntry1  |------------> +------------+
                       +------------+              |  func1()   |
                       |  VTEntry2  |--------+     |            |
                       +------------+        |     |            |
                       |      .     |        |     +------------+
                       |      .     |        |
                       |      .     |        +---->+------------+
                                                   |   func2()  |
                                                   |            |
                                                   |            |
                                                   +------------+

And this is how the class hierarchy looks like in the following code.
                  +------------------+
                  |     BaseClass    |
                  +------------------+
                    /             \
                   /               \
                  /                 \
                 V                   V
+------------------+             +------------------+
|    SubClass_1    |             |    SubClass_2    |
+------------------+             +------------------+

typedef void (*func)(void); // class member function type
typedef int* ptr;           // 32 bit system pointer type

class BaseClass {
private:
    int a; // DATA1

public:
    BaseClass() : a(0) {}

    // pure virtual function
    virtual void func1() =0;
    virtual void func2() =0;
    static void deref_vtable(ptr);
};

class SubClass_1 : public BaseClass {
public:
    void func1() {
        cout << "SubClass_1::func1" << endl;
    }

    void func2() {
        cout << "SubClass_1::func2" << endl;
    }
};

class SubClass_2 : public BaseClass {
public:
    void func1() {
        cout << "SubClass_2::func1" << endl;
    }

    void func2() {
        cout << "SubClass_2::func2" << endl;
    }
};

// static function to dereference the vtable entries
void BaseClass::deref_vtable(ptr vtable) {
    func pfunc = NULL;

    // dereferencing first (VTABLE1) entry in VTABLE
    pfunc = (func)*(vtable+0);
    pfunc();

    // dereferencing second (VTABLE2) entry in VTABLE
    pfunc = (func)*(vtable+1);
    pfunc();

    return;
}

int main(int argc, char * const argv[]) {
    BaseClass *pobj = NULL;
    ptr  vptr, vtable;

    // first sub class
    pobj = new SubClass_1;
    vptr = (ptr)pobj;     // first address of obj (class) points to vptr
    vtable = (ptr)*vptr;  // dereferencing vptr to get vtable

    cout << "vptr: " << vptr << "   " << "vtable: " << vtable << endl;
    BaseClass::deref_vtable(vtable);

    delete pobj;
    cout << endl;
 
    // second sub class
    pobj = new SubClass_2;
    vptr = (ptr)pobj;     // first address of obj (class) points to vptr
    vtable = (ptr)*vptr;  // dereferencing vptr to get vtable

    cout << "vptr: " << vptr << "   " << "vtable: " << vtable << endl;
    BaseClass::deref_vtable(vtable);

    delete pobj;
    return 0;
}

Output:
vptr: 0x9934008   vtable: 0x8048cf8
SubClass_1::func1
SubClass_1::func2

vptr: 0x9934008   vtable: 0x8048ce8
SubClass_2::func1
SubClass_2::func2

polymorphtest.cpp file link

No comments:

Post a Comment