Here is a simple C++ class, named A
:
class A
{
public:
explicit A() : m_a(0) { }
explicit A(int a) m_a(a) { }
int getA() const { return m_a; }
void setA(int a) { m_a = a; }
private:
int m_a;
}
This is what I know so far:
sizeof(A) = sizeof(int) = sizeof(m_a)
A
are stored somewhere in memory and all instances of class A
use the same member functions.This is what I don't know:
Where are member functions stored and how are they actually stored? Let's say that an int
for example is stored on 4 bytes; I can imagine the RAM memory layout with 4 contiguous cells each storing a part of that int. How can I imagine this layout for a function?(this could sound silly, but I imagine functions must have a place in memory because you can have a pointer point to them). Also how and where are function instructions stored? My first perception was that functions and function instructions are stored in the program executable(and its dynamic or static libraries) but if this is true what happens when you create a function pointer? AFAIK function pointers point to locations in RAM memory, can they point to locations in program binaries? If yes, how does this work?
Can anyone explain to me how this works and point out if what I know is right or wrong?
Normal member functions are stored in the . text section of your program. They do not take up extra memory per each instance, because a member function takes in a pointer to the class instance, rather than creating a whole new function for each instance.
All member functions of class A are stored somewhere in memory and all instances of class A use the same member functions.
Member functions or pointers to them aren't stored in the object. ( virtual functions are typically called through a pointer stored in a table to which an object has a single pointer to) This would be a huge waste of memory. They're typically stored in a code memory section, and are known to the compiler.
Class members are part of a class instance. Where they live depends upon where the instance lives. If you declare an instance of a class as an automatic variable, then it is on the stack. If you allocate it with operator new, then it is on the heap.
First, you need to understand the role of the linker and what are executables (usually executed in virtual memory) and address spaces & processes. On Linux, read about ELF and the execve(2) syscall. Read also Levine's Linkers & Loaders book and Operating Systems: Three Easy Pieces, and the C++11 standard n3337, and this draft report and a good C++ programming book, with this reference website.
Member functions can be virtual or plain functions.
A plain (non virtual
) member function is just like a C function (except that it has this
as an implicit, often first, parameter). For example your getA
method is implemented like the following C function (outside of the object, e.g. in the code segment of the binary executable) :
int C$getA(A*thisptr) const { return thisptr->m_a; }
then imagine that the compiler is translating p->getA()
into C$getA(p)
A virtual member function is generally implemented thru a vtable (virtual method table). An object with some virtual member functions (including destructor) has generally as its first (implicit) member field a pointer to such a table (generated elsewhere by the compiler). Your class A
don't have any virtual method, but imagine if it had an additional virtual void print(std::ostream&);
method, then your class A
would have the same layout as
struct A$ {
struct A$virtualmethodtable* _vptr;
int m_a;
};
and the virtual table might be
struct A$virtualmethodtable {
void (*print$fun) (struct A$*, std::ostream*);
};
(so adding other virtual functions means simply adding slot inside that vtable);
and then a call like p->print(std::cout);
would be translated almost like
p->_vptr.print$fun(p,&std::cout);
... In addition, the compiler would generate as constant tables various virtual method tables (one per class).
NB: things are more complex with multiple or virtual inheritance.
In both cases, member functions don't eat any additional space in the object. If it is non-virtual, it is just a plain function (in the code segment). If it is virtual, it shares a slot in the virtual method table.
NB. If you compile with a recent GCC (i.e. with g++
) or with a Clang (so clang++
) you could pass it e.g. the -fdump-tree-all
flag: it will produce hundreds of dump files showing partly -in a dumped textual form- some internal representations of the compiler, which you could inspect with a pager (e.g. less
) or a textual editor. You could also use MELT or look at the assembly code produced with g++ -S -fverbose-asm -O1
....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With