Shivahn wrote:So I've studied interpreted languages (well, interpreters), but not compilation. I have a quick question relating something I see in C++ to interpreted stuff, and I realize I may be way off.
Static member functions are said to be unrelated to any object of the class, and each member of a class has its own copy of non static functions. Does each member of a class have its own frame in the environment where the attributes and methods are defined, while static member functions are defined in the global environment? Or am I trying to apply some scheme that doesn't make sense onto a compiled language?
I'm not sure how to relate the idea of an interpreted-language style environment to compiled C++, especially as I'm not sure what interpreted languages you're familiar with.
First, everything I'm about to say deals with member variables only; I'll come back to functions at the end.
Basically each object has some block of memory. If you use automatic allocation (if it's a local), then that block is on the stack; if you use new, then it's in the heap; if it's a global, than it's in the "static" area. (More on that in a sec.) Wherever it is, that block of memory has one slot for each (non-static) member variable, along possibly with some padding and a vptr (more on that in a sec too).
Depending on what you're familiar with, you
might be able to call that a frame in your environment.
Class-static variables are essentially globals. They're allocated along with globals in the same area of memory. The only difference is the namespace it's in and how it's referred to; a static variable
x in a class
C might essentially be desugared to just talking about some global
C__x or something. (In fact you can see this in real-compiler mangled names, they just use more complicated schemes.) You could probably consider the globals the top-level environment if you want.
Functions are different. Non-virtual functions are basically the same no matter whether they are static or not. They're put in the same place, and objects have no reference to them. The binding to those functions are done statically, so there's no reference needed. The only difference between a static function and a non-virtual instance function is that the instance function will have an implicit "this" parameter.
Virtual functions are a bit different because of the runtime binding. The actual function code is still put in the same place, but now objects contain references to them. Each
class C (not object) gets something called a "vtable" that has pointers to the functions in question. (Unlike most interpreted languages this isn't a map from name->function, just an array; the offset from the beginning of the vtable is known at compile time, and subclasses keep the same layout of their vtable as their parent classes for the parts where they overlap.) Then each object has what's called a "vpointer" to its class's vtable. When
a.foo() executes, the code will look at a's vptr, follow it to the vtable, look up the offset of foo, then call that address.
(Technically the stuff in this post is implementation details, but practically speaking it's universally true. Different compilers can use different vtable layouts, but as this is a very efficient way of doing things and it's perfectly satisfactory as far as language semantics goes, this is what everyone does.)