C++ Internals handling this pointer

M

mailtogops

Hi,

I have this question in mind for many years..

C++ class provides encapsulation which encapsulate its members and
member function.

class Experiment
{
private:

int _experient;

public:

void ExpFun()
{
// Code here
}

};

Here ExpFun() is a member function, so at runtime this member function
shall have a memory where the function execuatble code reside.
Obviously there should be only one copy (Assume void ExpFun() is not
inline function) of ExpFun() in memory.

But I can create many objects for Experiment class

Experiment e1,e2, e3;

Logically we are calling the function like this

e1.ExpFun();
e2.ExpFun();
e3.ExpFun();


When the function is called, what happens?. Howcome the ExpFun() works
exactly with the corresponding member variables of the object?. Where
this pointer has been passed to ExpFun()?.

I would appreciate if someone answer me or guide a good book to get
idea about this..

Thanks for you time.

Thanks & Regards,

Gopal
 
N

Niels Dybdahl

When the function is called, what happens?. Howcome the ExpFun() works
exactly with the corresponding member variables of the object?. Where
this pointer has been passed to ExpFun()?.

Yes. The pointer to the object (this) is passed to the function. So the
compiler implements a function which actually takes one more parameter than
you have specified.

Niels Dybdahl
 
N

Neelesh Bodas

Hi,

class Experiment
{
private:

int _experient;

public:

void ExpFun()
{
// Code here
}

};

Here ExpFun() is a member function, so at runtime this member function
shall have a memory where the function execuatble code reside.
Obviously there should be only one copy (Assume void ExpFun() is not
inline function) of ExpFun() in memory.

By the way, functions defined inside the class are by default "inline".
(Of course that doesnot mean that they will _always_ be inlined)
So you could define the same function outside the class.
But I can create many objects for Experiment class

Experiment e1,e2, e3;

Logically we are calling the function like this

e1.ExpFun();
e2.ExpFun();
e3.ExpFun();


When the function is called, what happens?. Howcome the ExpFun() works
exactly with the corresponding member variables of the object?. Where
this pointer has been passed to ExpFun()?.

When a non-static member function is called, the "this" pointer is
_always_ passed as the "hidden" argument. So Suppose your member
function X::foo has the prototype :

void X::foo(int, int) ;

then when you say
p.foo(14,20)
for some object p of class X, the function foo is actually invoked with
three parameters (as an example : )

1) the this pointer
2) first argument's copy (14)
3) second argument's copy (20)

I would appreciate if someone answer me or guide a good book to get
idea about this..

You can actually try to compile a sample C++ program to generate
assembly code and look at it yourself.
 
N

Niklas Norrthon

Hi,

I have this question in mind for many years..

C++ class provides encapsulation which encapsulate its members and
member function.

class Experiment
{
private:

int _experient;

public:

void ExpFun()
{
// Code here
}

};

Here ExpFun() is a member function, so at runtime this member function
shall have a memory where the function execuatble code reside.
Obviously there should be only one copy (Assume void ExpFun() is not
inline function) of ExpFun() in memory.

But I can create many objects for Experiment class

Experiment e1,e2, e3;

Logically we are calling the function like this

e1.ExpFun();
e2.ExpFun();
e3.ExpFun();


When the function is called, what happens?. Howcome the ExpFun() works
exactly with the corresponding member variables of the object?. Where
this pointer has been passed to ExpFun()?.

I would appreciate if someone answer me or guide a good book to get
idea about this..

Short answer first: By magic. The standard doesn't say how things should
work. The internals is implementation specific.

Below is a description of one possible way to implement the C++ object
model, which might not be 100% correct on any single existing platform
but should give you an idea on how things work in practice.

The standard speaks about different storage classes of memory, and there
are three of them: static, automatic, and free store.

Static memory is initialized at the beginning of a program, and is
never destroyed, deallocated or any such thing. Objects can be placed
in static memory, either by defining them at namespace scope, i.e
outside any function, or struct (when I say struct it also applies to
classes), or by using the static keyword inside functions and structs.

Executable code also resides in static memory. This applies to normal
functions as well as member functions, constructors, destructors etc.
So when you say something like:

struct Experiment {
void ExpFun() { /* code here */ }
};

The compiler generates the code at one place in the static memory and
label it with a name according to an implementation specific scheme.
For example it could use the name __struct_Experiment_void_ExpFun_void__
for this function.

When you later in your code call it with:

int main()
{
Experiment e;
e.ExpFun();
}

The compiler knows that e is of the type Experiment, so it just translate
the code to call the function named __struct_Experiment_void_ExpFun_void__,
almost as you would have said:

extern __struct_Experiment_void_ExpFun_void__();

int main()
{
__struct_Experiment_void_ExpFun_void__();
}

The exact naming scheme is specific to each implementation, but it's usually
quite easy to list the names. On unix systems there is the nm utility and
I guess Win32's dumpbin is similar. Sometimes the "mangled" names show up
in error messages during the linking phase too, so it can be useful to
have an idea of how things work.

So reamins the question of how the function knows where the member variables
are stored.

Suppose now that we have the following:

struct Experiment {
int member;
static int member_static;

Experiment() : member(0) { }
void ExpFun() { member_static = member; }
void ExpFun(int x) { member = x; }
static void ExpStaticFun(int x);
};

int Experiment::member_static = 42;

The compiler takes this apart and generates code for the following:

struct Experiment {
int member;
};

int __struct_Experiment_member_static__ = 42;

void __struct_Experiment_ctor_void__(Experiment * const this)
{
this->member = 0;
}

void __struct_Experiment_ExpFun_void__(Experiment * const this)
{
__struct_Experiment_member_static__ = this->member;
}

void __struct_Experiment_ExpFun_int__(Experiment * const this, int x)
{
this->member = x;
}

void __struct_static_Experiment_ExpFun_int__(int x)
{
__struct_Experiment_member_static__ = x;
}

All member functions and static member variables are stripped out
from the struct, leaving an old fashoned C struct behind. Taking
the sizeof a struct reflects this. It does not matter how many
member functions a struct has, the size is the same.

The member functions are renamed and placed next to all other
functions in the static memory, and a function argument named
"this", is automatically added, which holds a pointer to the
object the function will operate on. Note that it is only for
non static member functions that the "this" argument is added.

When a client of the struct later says:

int main()
{
Experiment e;
e.ExpFun();
e.ExpFun(12);
e.ExpStaticFun(13);
Experiment::ExpStaticFun(14);
}

It gets translated to:

int main()
{
Experiment e; /* sizeof e is allocated in automatic memory (stack) */
__struct_Experiment_ctor_void__(&e);
__struct_Experiment_ExpFun_void__(&e);
__struct_Experiment_ExpFun_int__(&e, 12);
__struct_static_Experiment_ExpFun_int__(13);
__struct_static_Experiment_ExpFun_int__(14);
}

I hope you get an idea on how things usually are implemented.
Play with your compiler and tools to see how it works in your
implementation.

/Niklas Norrthon
 
M

mailtogops

Hi All,

Thanks for the information. But furthur I would like ot dig more..

The current object pointer always passed to the member function.
Suppose consider the class

class Experiment
{
public:
int _no;
void setNo(int i)
{
_no = i;
}
};

In the above code, I have assigned

_no = i;

So during compilation, the compiler must convert this code to represent
internally like

this->_no = i;

Is this right?

Thanks & Regards,

Gopal
 
N

Neelesh Bodas

Hi All,

In the above code, I have assigned

_no = i;

So during compilation, the compiler must convert this code to represent
internally like

this->_no = i;

Is this right?

Yes, thats what the compiler does.
Look up any descent book on C++, it should talk about this. Eg. TC++PL
10.2.7
 
M

mailtogops

Hi Niklas Norrthon,

Thanks a lot for detailed explanation..

Could you please point out how the compiler manages dynamic
call(virtual functions/polymorphism) with the 'this' pointer, which
would complete this discussion..

Thanks & Regards,

Gopal
 
A

Alf P. Steinbach

N

Niklas Norrthon

Hi Niklas Norrthon,

Thanks a lot for detailed explanation..

Could you please point out how the compiler manages dynamic
call(virtual functions/polymorphism) with the 'this' pointer, which
would complete this discussion..

By magic. :)

As always different compilers do it in different ways. On common
approach is to have a "virtual table (or vtable)" for each class,
and have a "virtual pointer (vptr)" included in each instance
of a class with at least one virtual function.

struct Foo
{
Foo();
virtual ~Foo() { }

void func();
virtual void grunc();

int data;
};

For the Foo class above, the compiler would generate the
functions:

__struct_Foo_ctor_void__(Foo * const this);
__struct_Foo_virtual_dtor__(Foo * const this);
__struct_Foo_void_func_void__(Foo * const this);
__struct_Foo_virtual_void_grunc_void__(Foo * const this);

The virtual table would be implemented by a global varible:

struct __Foo_vtable_t__
{
void (*__dtor__)(Foo* const);
void (*__grunc__)(Foo* const);
} __Foo_vtable__ = { __struct_Foo_virtual_dtor__,
__struct_Foo_virtual_void_grunc_void__ };

Finally, the data in a Foo object could be represented by the following:

struct Foo
{
__Foo_vtable_t__* vptr;
int data;
};

The code:

void hello(Foo* f)
{
f->func();
f->grunc();
f->Foo::grunc();
}

int main()
{
Foo x;
hello(&f);
return 0;
}

would be transformed to something like:

void hello(Foo* f)
{
__struct__Foo_void_func_void__(f);
f->vptr->__grunc__(f);
__struct__Foo_virtual_void_grunc_void__(f);
}

int main()
{
struct Foo x;
__struct_Foo_ctor_void__(f);
hello(f);
__struct_Foo_virtual_dtor__(f);
return 0;
}

When there is inheritance involved, each derived class has it's
own vtable, with overridden functions at the same places as in
the parent vtable, with additional function appended:

struct Bar : public Foo
{
virtual ~Bar() { }
virtual void beepbeep();
};

struct __Bar_vtable_t__
{
void (*__dtor__)(Foo* const);
void (*__grunc__)(Foo* const);
void (*__beepbeep__)(Foo* const);
} __Foo_vtable__ = { __struct_Bar_virtual_dtor__,
__struct_Foo_virtual_void_grunc_void__,
__struct_Bar_virtual_void_beepbeep_void__};


For functions that are not overridden the pointer in the vtable point
to the parent function.

Multiple inheritance is a little bit more involved, and virtual
inheritance even worse, so I won't talk about them here, but
when you understand normal inheritance, it's not too difficult
to understand how these work too.

/Niklas Norrthon
 
M

mailtogops

Niklas Norrthon,

Thanks a lot for your detailed explanation..

It is really nice to use usenet which is like thesaurus of knowledge
and people value..

Enjoy Learning..

Thanks and Regards,

Gopal
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,141
Latest member
BlissKeto
Top