Well as Chuck has given you a hint, the function is not stored inside the object itself. The this pointer is the key to all. The "this" pointer is passed to the function as the first argument when it is called.
So lets take an example the class SampleCode.
class SampleCode
{
private:
int _int;
public:
void foo()
{
++_int;
}
void bar()
{
int i = 0;
}
};
void caller()
{
SampleCode* p = NULL;
p->foo();
p->bar();
}class SampleCode
{
private:
int _int;
public:
void foo()
{
++_int;
}
void bar()
{
int ibar = 0;
}
static void foo_bar()
{
int ifoo_bar = 0;
}
};
void called()
{
int icalled = 0;
}
void caller()
{
SampleCode* p = 0;
p->foo();
p->bar();
p->foo_bar();
}
So the algorithm is that this code is converted to something like:
1. All Function (Member or not) are executable, but the code does not change. So store them in the Text segment of the output object file. Text segments are read only segments.
2. When a function is called the parameters are passed to it by stack. So when a non static member function is called then the
this
pointer is pushed into the stack behind the scene.
3. Inside the member function any member variable is referenced relative to the
this
pointer.
4. This is not passed to the static member functions. This is the reason static functions cannot use any member variable. Cause they do not contain a reference to
this
the don't know where the member variable resides in the code.
When we see the example of the code
p->foo()
Now suppose you have allocated the object. So after the constructor call is done and "p" points to a proper memory location, the this will also be know. So when we call the function something like this happens.
A. Push
this
to the stack.
B. Call
foo
.
foo( )
uses a member variable,
but here we made a mistake, we did not allocate the pointer. So "this" basically means a garbage value. So this line will crash, as the code tries to find _int variable relative to "
this
" pointer.
5. Now lets consider the example
p->bar()
(This line will not crash)
We are calling a function here, but we are not using a member variable. So we don't mind if the
this
pointer is garbage.
This will not cause a crash. The code for function is not stored in the allocated object that's why, if it were stored then there would have been a crash.
Now we will compile it:
Allow me to use a real assembled output.(Visual Studio 2008)
Just see the bold lines
PUBLIC ?called@@YAXXZ ; Function called declared
_TEXT SEGMENT ;TEXT segment, code for function called
_icalled$ = -4 ; size = 4
?called@@YAXXZ PROC ; called, COMDAT
; 24 : {
push ebp
mov ebp, esp
push ecx
; 25 : int icalled = 0;
mov DWORD PTR _icalled$[ebp], 0
; 26 : }
mov esp, ebp
pop ebp
ret 0
?called@@YAXXZ ENDP ; Code for called Ends here
_TEXT ENDS
;Member function definition starts here
PUBLIC ?foo_bar@SampleCode@@SAXXZ ; SampleCode::foo_bar
PUBLIC ?bar@SampleCode@@QAEXXZ ; SampleCode::bar
PUBLIC ?foo@SampleCode@@QAEXXZ ; SampleCode::foo
;Member function definition ends here
;Function caller
definition starts here
PUBLIC ?caller@@YAXXZ ; caller
;Function caller
definition ends here
; function caller
TEXT segment, i.e. code start
_TEXT SEGMENT
_p$ = -4 ; size = 4
?caller@@YAXXZ PROC ; caller, COMDAT
; 29 : {
push ebp
mov ebp, esp
push ecx
; 30 : SampleCode* p = 0;
mov DWORD PTR _p$[ebp], 0
; 31 : p->foo();
mov ecx, DWORD PTR _p$[ebp]
;this is ecx register
call ?foo@SampleCode@@QAEXXZ ; SampleCode::foo
; 32 : p->bar();
mov ecx, DWORD PTR _p$[ebp]
call ?bar@SampleCode@@QAEXXZ ; SampleCode::bar
; 33 : p->foo_bar(); No this is passed for static function
call ?foo_bar@SampleCode@@SAXXZ ; SampleCode::foo_bar
; 34 : }
mov esp, ebp
pop ebp
ret 0
?caller@@YAXXZ ENDP
; function
caller
TEXT segment , i.e. code end
; Function compile flags: /Odtp
_TEXT ENDS
; COMDAT ?foo@SampleCode@@QAEXXZ
_TEXT SEGMENT
_this$ = -4 ; size = 4
?foo@SampleCode@@QAEXXZ PROC
; SampleCode::foo, COMDAT
; _this$ = ecx
; 8 : {
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
;This is populated from the stack as the function SampleCode::foo is called
; 9 : ++_int;
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR [eax]
add ecx, 1
mov edx, DWORD PTR _this$[ebp]
mov DWORD PTR [edx], ecx
; 10 : }
mov esp, ebp
pop ebp
ret 0
?foo@SampleCode@@QAEXXZ ENDP ; SampleCode::foo
; Function compile flags: /Odtp
_TEXT ENDS
; COMDAT ?bar@SampleCode@@QAEXXZ
_TEXT SEGMENT
_this$ = -8 ; size = 4
_ibar$ = -4 ; size = 4
?bar@SampleCode@@QAEXXZ PROC ; SampleCode::bar, COMDAT
; _this$ = ecx
; 13 : {
push ebp
mov ebp, esp
sub esp, 8
mov DWORD PTR _this$[ebp], ecx
; 14 : int ibar = 0;
mov DWORD PTR _ibar$[ebp], 0
; 15 : }
mov esp, ebp
pop ebp
ret 0
?bar@SampleCode@@QAEXXZ ENDP ; SampleCode::bar
; Function compile flags: /Odtp
_TEXT ENDS
; COMDAT ?foo_bar@SampleCode@@SAXXZ
_TEXT SEGMENT
_ifoo_bar$ = -4 ; size = 4
?foo_bar@SampleCode@@SAXXZ PROC ; SampleCode::foo_bar, COMDAT
; 18 : {
push ebp
mov ebp, esp
push ecx
; 19 : int ifoo_bar = 0;
mov DWORD PTR _ifoo_bar$[ebp], 0
; 20 : }
mov esp, ebp
pop ebp
ret 0
?foo_bar@SampleCode@@SAXXZ ENDP ; SampleCode::foo_bar
_TEXT ENDS
END