Does every object of virtual class have a pointer to vtable?
Or only the object of base class with virtual function has it?
Where did the vtable stored? code
Like someone else said, the C++ Standard does not mandate a virtual method table, but allows one to be used. I've done my tests using gcc and this code and one of the simplest possible scenario:
class Base {
public:
virtual void bark() { }
int dont_do_ebo;
};
class Derived1 : public Base {
public:
virtual void bark() { }
int dont_do_ebo;
};
class Derived2 : public Base {
public:
virtual void smile() { }
int dont_do_ebo;
};
void use(Base* );
int main() {
Base * b = new Derived1;
use(b);
Base * b1 = new Derived2;
use(b1);
}
Added data-members to prevent the compiler to give the base-class a size-of of zero (it's known as the empty-base-class-optimization). This is the layout that GCC chose: (print using -fdump-class-hierarchy)
Vtable for Base
Base::_ZTV4Base: 3u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI4Base)
8 Base::bark
Class Base
size=8 align=4
base size=8 base align=4
Base (0xb7b578e8) 0
vptr=((& Base::_ZTV4Base) + 8u)
Vtable for Derived1
Derived1::_ZTV8Derived1: 3u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI8Derived1)
8 Derived1::bark
Class Derived1
size=12 align=4
base size=12 base align=4
Derived1 (0xb7ad6400) 0
vptr=((& Derived1::_ZTV8Derived1) + 8u)
Base (0xb7b57ac8) 0
primary-for Derived1 (0xb7ad6400)
Vtable for Derived2
Derived2::_ZTV8Derived2: 4u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI8Derived2)
8 Base::bark
12 Derived2::smile
Class Derived2
size=12 align=4
base size=12 base align=4
Derived2 (0xb7ad64c0) 0
vptr=((& Derived2::_ZTV8Derived2) + 8u)
Base (0xb7b57c30) 0
primary-for Derived2 (0xb7ad64c0)
As you see each class has a vtable. The first two entries are special. The second one points to the RTTI data of the class. The first one - i knew it but forgot. It's got some use in more complicated cases. Well, as the layout shows, if you have an object of class Derived1, then the vptr (v-table-pointer) will point to the v-table of class Derived1 of course, which has exactly one entry for its function bark pointing to Derived1's version. Derived2's vptr points to Derived2's vtable, which has two entries. The other one is the new method that's added by it, smile. It repeats the entry for Base::bark, which will point to Base's version of the function of course, because it's the most derived version of it.
I've also dumped the tree that's generated by GCC after some optimizations are done (constructor inlined, ...), with -fdump-tree-optimized. The output is using GCC's middle-end language GIMPL which is front-end independent, indented into some C-like block structure:
;; Function virtual void Base::bark() (_ZN4Base4barkEv)
virtual void Base::bark() (this)
{
:
return;
}
;; Function virtual void Derived1::bark() (_ZN8Derived14barkEv)
virtual void Derived1::bark() (this)
{
:
return;
}
;; Function virtual void Derived2::smile() (_ZN8Derived25smileEv)
virtual void Derived2::smile() (this)
{
:
return;
}
;; Function int main() (main)
int main() ()
{
void * D.1757;
struct Derived2 * D.1734;
void * D.1756;
struct Derived1 * D.1693;
:
D.1756 = operator new (12);
D.1693 = (struct Derived1 *) D.1756;
D.1693->D.1671._vptr.Base = &_ZTV8Derived1[2];
use (&D.1693->D.1671);
D.1757 = operator new (12);
D.1734 = (struct Derived2 *) D.1757;
D.1734->D.1682._vptr.Base = &_ZTV8Derived2[2];
use (&D.1734->D.1682);
return 0;
}
As we can see nicely, it's just setting one pointer - the vptr - which will point to the appropriate vtable we have seen before when creating the object. I've also dumped the assembler code for the creation of the Derived1 and call to use ($4 is first argument register, $2 is return value register, $0 is always-0-register) after demangling the names in it by the c++filt tool :)
# 1st arg: 12byte
add $4, $0, 12
# allocate 12byte
jal operator new(unsigned long)
# get ptr to first function in the vtable of Derived1
add $3, $0, vtable for Derived1+8
# store that pointer at offset 0x0 of the object (vptr)
stw $3, $2, 0
# 1st arg is the address of the object
add $4, $0, $2
jal use(Base*)
What happens if we want to call bark?:
void doit(Base* b) {
b->bark();
}
GIMPL code:
;; Function void doit(Base*) (_Z4doitP4Base)
void doit(Base*) (b)
{
:
OBJ_TYPE_REF(*b->_vptr.Base;b->0) (b) [tail call];
return;
}
OBJ_TYPE_REF is a GIMPL construct which is pretty printed into (it's documented in gcc/tree.def in the gcc SVN source-code)
OBJ_TYPE_REF(; -> )
It's meaning: Use the expression *b->_vptr.Base on the object b, and store the frontend (c++) specific value 0 (it's the index into the vtable). Finally, it's passing b as the "this" argument. Would we call a function that appears at the 2nd index in the vtable (note, we don't know which vtable of which type!), the GIMPL would look like this:
OBJ_TYPE_REF(*(b->_vptr.Base + 4);b->1) (b) [tail call];
Of course, here the assembly code again (stack-frame stuff cut off):
# load vptr into register $2
# (remember $4 is the address of the object,
# doit's first arg)
ldw $2, $4, 0
# load whatever is stored there into register $2
ldw $2, $2, 0
# jump to that address. note that "this" is passed by $4
jalr $2
Remember that the vptr points exactly at the first function. (Before that entry the RTTI slot were stored). So, whatever appears at that slot is called. It's also marking the call as tail-call, because it happens as the last statement in our doit function.