I’m always confused about the memory layout of the c++ virtual table.Here is an example code
i use to study it:
#include <cstdio>
#include <iostream>
using namespace std;
class Point
{
public:
Point()
{
cout<<"Point constructor"<<endl;
}
virtual void func_hs()
{
cout<<"Point::func_hs"<<endl;
printf("the address of this --func_hs:%p\n",&Point::func_hs);
}
virtual void func_zzy()
{
cout<<"Point::func_zzy"<<endl;
printf("the address of this --func_zzy:%p\n",&Point::func_zzy);
}
void printVt()
{
printf("the address of object,this:%p\nthe address of vt:%p\n",
this,(void*)*(int*)this);
}
void callVtFuncs(int num=2)
{
typedef void (*Funp)(void);
for(int i=0;i<num;i++)
{
Funp funp=(Funp)*((int*)*(int*)this+i);
printf("%p\n",((int*)*(int*)this+i));
printf("Point::callVtFuncs=>address of this fun:%p\n",funp);
if(i==2||i==3)
{
continue;
}
funp();
}
}
void printVirtualFunAddress()
{
cout<<endl<<endl;
printf("func_hs:%p\nfunc_zzy:%p\n",&Point::func_hs,&Point::func_zzy);
}
virtual ~Point()
{
cout<<"Point destructor"<<endl;
}
virtual void func_zzzy()
{
cout<<"Point::func_zzzy"<<endl;
printf("the address of this --func_zzzy:%p\n",&Point::func_zzzy);
}
protected:
float x,y,z;
};
int main(int argc, char *argv[])
{
Point point;
point.printVt();
point.callVtFuncs(5);
point.printVirtualFunAddress();
return 0;
}
I put 4 virtual functions on the class,and print out there address information.Here is the outout:
Point constructor
the address of object,this:0xbffff620
the address of vt:0x8048db8
0x8048db8
Point::callVtFuncs=>address of this fun:0x8048914
Point::func_hs
the address of this --func_hs:0x1
0x8048dbc
Point::callVtFuncs=>address of this fun:0x8048966
Point::func_zzy
the address of this --func_zzy:0x5
0x8048dc0
Point::callVtFuncs=>address of this fun:0x8048b0a
0x8048dc4
Point::callVtFuncs=>address of this fun:0x8048b56
0x8048dc8
Point::callVtFuncs=>address of this fun:0x8048b74
Point::func_zzzy
the address of this --func_zzzy:0x11
func_hs:0x1
func_zzy:(nil)
func_zzzy:0x5
Point destructor
and I’m totally don’t understand why the last output is ‘funz_zzy:(nil)’ and ‘funz_zzy:0x5’
but the above is 0x5 and 0x11.
Here is some debug information:(linux 32bit)
(gdb) x/16a 0x8048da8
0x8048da8: 0xa7025 0x0 0x0 0x8048dd4 <_ZTI5Point>
0x8048db8 <_ZTV5Point+8>: 0x8048914 <Point::func_hs()> 0x8048966 <Point::func_zzy()> 0x8048b0a <Point::~Point()> 0x8048b56 <Point::~Point()>
0x8048dc8 <_ZTV5Point+24>: 0x8048b74 <Point::func_zzzy()> 0x696f5035 0x746e 0x804a248 <_ZTVN10__cxxabiv117__class_type_infoE@@CXXABI_1.3+8>
0x8048dd8 <_ZTI5Point+4>: 0x8048dcc <_ZTS5Point> 0x3b031b01 0x80 0xf
I can’t figure out why there are two Point::~Point()? And is the information at 0x804a248 stand for the type info of the class?
some other information:
(gdb) x/1a 0x8048dd4
0x8048dd4 <_ZTI5Point>: 0x804a248 <_ZTVN10__cxxabiv117__class_type_infoE@@CXXABI_1.3+8>
what is 0x8048dd4 used for?
This is because in one case you use
printf()with multiple arguments, while in others only with one. And since pointer to members seems to be 8 bytes length type you get this extra zeroes. Try this code:for me it prints
Looks like this is connected with that it’s virtual. If you remove
virtualkeyword then only one destructor is generated. The second one is the same as the first one, but it additionally callsfree()function on some data. I didn’t figure out what’s this for.This should be the structure you get as the result of
typeid(Point).