virtuals
so i’m back with more of the shit that is C++ learning. i’ve gotten the hang of typing “cout” now instead of “printf” but more about this some other time. recently, i’m learning about virtual functions and i have this as an example:
class animal {
public:
void normal() { }
virtual void normvirtual() { }
virtual void purevirtual() = 0;
};
class sheep : public animal {
public:
void normvirtual() {
cout<<"sheep from normvirtual"<<endl;
}
void purevirtual() {
cout<<"sheep from purevirtual"<<endl;
}
};
again, using IDA and objdump …
sheep judy; 8048803: 8d 45 e8 lea eax,[ebp-24] 8048806: 50 push eax 8048807: e8 8a 00 00 00 call 8048896 <sheep::sheep()>
nothing much happening here. the usual thing. a *this pointer being passed to a default constructor as a hidden argument.
08048896 <sheep::sheep()>: 8048896: 55 push ebp ; save current stack frame 8048897: 89 e5 mov ebp,esp ; new stack frame 8048899: 83 ec 08 sub esp,0x8 ; space 804889c: 83 ec 0c sub esp,0xc ; space 804889f: ff 75 08 push DWORD PTR [ebp+8] ; our *this pointer 80488a2: e8 4d 00 00 00 call 80488f4 <animal::animal()> ; call animal constructor 80488a7: 83 c4 10 add esp,0x10 ; cleanup 80488aa: 8b 45 08 mov eax,DWORD PTR [ebp+8] ; dereference 80488ad: c7 00 80 8a 04 08 mov DWORD PTR [eax],0x8048a80 ; see below... 80488b3: c9 leave ; epilog 80488b4: c3 ret ; return to callee 80488b5: 90 nop ; byte pad (no operation)
so what does mov DWORD PTR [eax],0x8048a80 do? following the memory address in ida gives me an array of sheep class functions. objdump tells me that this block is called a “vtable” for my sheep class. 0x8048a80 is prolly what they call the “VPTR” pointer.
and then i called the get_kind() function.
get_kind(judy); 8048812: 8d 45 e8 lea eax,[ebp-24] 8048815: 50 push eax 8048816: e8 9b 00 00 00 call 80488b6 <get_kind(animal&)>
i learned from prior readings about “upcasting” and how a derived class object can be assigned to base class object. and this is how i interpreted things…
inline void get_kind(animal &a)
{
a.normal();
a.normvirtual();
a.purevirtual();
}
080488b6 <get_kind(animal&)>:
80488b6: 55 push ebp
80488b7: 89 e5 mov ebp,esp ; prolog
80488b9: 83 ec 08 sub esp,0x8
80488bc: 83 ec 0c sub esp,0xc ; spaces
80488bf: ff 75 08 push DWORD PTR [ebp+8] ; object as hidden argument
80488c2: e8 3b 00 00 00 call 8048902 <animal::normal()> ; call the animal::normal() method
80488c7: 83 c4 10 add esp,0x10 ; cleanup
80488ca: 83 ec 0c sub esp,0xc ; space
80488cd: 8b 45 08 mov eax,DWORD PTR [ebp+8] ; *this pointer (VPTR)
80488d0: 8b 00 mov eax,DWORD PTR [eax] ; dereference
80488d2: ff 75 08 push DWORD PTR [ebp+8] ; pass object again as hidden argument
80488d5: 8b 00 mov eax,DWORD PTR [eax] ; derefence again
80488d7: ff d0 call eax ; call the first virtual function
80488d9: 83 c4 10 add esp,0x10 ; cleanup
80488dc: 83 ec 0c sub esp,0xc ; space
80488df: 8b 45 08 mov eax,DWORD PTR [ebp+8] ; *this pointer (VPTR)
80488e2: 8b 00 mov eax,DWORD PTR [eax] ; dereference
80488e4: 83 c0 04 add eax,0x4 ; move to next function in array of funtions
80488e7: ff 75 08 push DWORD PTR [ebp+8] ; pass object as hidden argument
80488ea: 8b 00 mov eax,DWORD PTR [eax] ; dereference
80488ec: ff d0 call eax ; call second virtual function
80488ee: 83 c4 10 add esp,0x10 ; cleanup
80488f1: c9 leave ; epilog
80488f2: c3 ret ; return to callee
80488f3: 90 nop ; byte pad (no operation)
i wonder if i got the above things right? dynamic binding entails an additional overhead of locating and assigning the correct memory address for a particular function call to resolve. the whole thing is somehow similar to ELF PLT.GOT fixup (executable and linkable format procedure linkage table global offset table) except that addresses are always being referenced from the VPTR whenever a virtual call will be made.
also, there is a subtle difference between a normal virtual and a pure virtual function. from the looks of things, the pure virtual function takes up no space. in fact, i didn’t even see any single declartion of animal::purevirtual() anywhere on the dissassembly.

The book you need is Lipman’s “Inside the C++ Object Model”. Your simple test cases aren’t enough to illustrate what’s really going on — what you’re seeing for pure virtuals isn’t true in the general case.
Comment by Ciaran McCreesh — April 5, 2007 @ 5:25 pm
thanks for the tip! it’s about time i follow a learning structure of sorts.
hopefully, i can create more complex test cases as i gain confidence… maybe a few months from now…
Comment by sleepy jenkins — April 5, 2007 @ 6:04 pm
Wow, that’s a different way of finding out and confirming the fact that dynamic polymorphic types are chock full of overhead.
Perhaps you’ll want to see how other approaches at a higher level will actually be practically useful for whole software projects and whatnot. Performance is not everything you know.
Comment by Dean Michael Berris — April 23, 2007 @ 10:22 pm
dean michael berris:
prolly not the most intelligent thing to do imho.. almost to the point of being a masochist.. ..but it’s one of the many things i really enjoy doing. books and a good advice here and there are always very welcome too. thanks!
Comment by sleepy jenkins — April 25, 2007 @ 5:42 pm