As everybody knows, a "VTable" (short for "virtual table") is a data
Structure used to lookup methods when they are called dynamically. In
C++, it is generated by the compiler. If there is a problem in
Vtable, it's a bit tricky to know the reasons.
I had the following problem with Vtable :-
In mutiple hierarchical implementation, create an object from derived class and typecast into genric pointer then upcast that genric pointer into one of parent class object pointer. And then invoke a late binding method, the Vtable internally will call a different 'method name'. Let discuss this problem in details. The sample code
is shown in below.
///////////////////////////////////////vtbltest.h///////////////////////////
#ifndef VTBL_TEST_H
#define VTBL_TEST_H
#include
using namespace std;
{
public:
virtual ~A();
virtual void fun () = 0;
virtual void act () = 0;
virtual void show () = 0;
};
class B
{
public:
virtual ~B();
virtual void draw () = 0;
};
class C : public B, public A
{
public:
virtual ~C();
virtual void fun();
virtual void act();
virtual void show();
virtual void draw();
};
#endif
////////////////////////////////////////////////////////vtbltest.C//////////
#include "vtbltest.h"
A::~A()
{ cout<<"\nA::~A";
B::~B()
{ cout<<"\nB::~B";} C::~C() {
void C::act()
{
void C::show()
{
void C::draw()
{
int main ()
{
class C *pC = new C ();
//assign to generic pointer
void *pVoid = (void*) pC;
//assign generic pointer to the class A pointer.
//Note: Class A is Base class of Class C
class A *pA = reinterpret_cast
//invoke a show method.
pA->show();
delete pA;
return 0 ;
}
The abstract class 'A' and 'B' are inherited by the concrete class
'C'. Create an instance of C, typecast it to generic pointer, then
re-typecast the generic pointer to either base class 'A' or 'B' and
then invoke a method 'show'.
The expected output is:
C::show
C::~C
A::~A
B::~B
C::act
C::~C
A::~A
B::~B
We end up with a big mess. Let us understand this misbehavior by knowing what is happening with the virtual table. We will debug with gdb ...
[amaran@buildsrv c++]$ gdb ./a.out
...
...
...
(gdb) b main
Breakpoint 1 at 0x8048bc6: file vtbltest.C, line 41.
(gdb) r
Starting program: /home1/amaran/learning/c++/a.out
Breakpoint 1, main () at vtbltest.C:41
41 class C *pC = new C ();
(gdb) n
42 void *pVoid = (void*) pC;
(gdb) n
43 class A *pA = (class A*)pVoid;
(gdb) n
45 pA->show();
(gdb) p pC
$1 = (C *) 0x804b008
(gdb) p pVoid
$2 = (void *) 0x804b008
(gdb) p pA
$3 = (A *) 0x804b008
(gdb) p *pC
$4 = {<\B> = {_vptr.B = 0x804a148},
<\A> = {_vptr.A = 0x804a168},
(gdb) p *pVoid
Attempt to dereference a generic pointer.
(gdb) p *pA
$5 = {_vptr.A = 0x804a148}
(gdb)
Look at the gdb trace at $4 and $5. It shows the virtual table pointer values. The pointer pC shows that the virtual table address of 'class B' is "_vptr.B = 0x804a148" and 'Class A' is "_vptr.A = 0x804a168". After typecasting of generic pointer and re-typecasting the pVoid to class A pointer, the class A pointer's virtual table address is "_vptr.A =0x804a148". Now compare pC's virtual table address with that of pA's. They do do not match (pC->"_vptr.A = 0x804a168" not equal to
pA->"_vptr.A =0x804a148").
What are the possible errors with the code?
1) Cast from Generic pointer to Class A pointer
2) Even if do above step, why vtable address is wrongly assigned
(possible vtable implementation leak)
3) Inheritance methodologies
Solutions:
1) Avoid cast to generic pointer in case of multiple inheritance.
2) if its indeed for existing desgin, first upcast object into base class pointer then type cast into genric pointer.
What might be happening the implementation of vtable:
When we do down cast to base class, the first virtual table pointer is assigned to that base class's vtable pointer. That is pC->vptrB address is assigned to pA->vptrA. The above code will work, if we upcast into class B not class A.
No comments:
Post a Comment