Hi wanderer83,
Thanks for accepting my proposal.
The book I mentioned is "Inside the C++ Object Model" by Stanley B.
Lippman (Addison-Wesley Professional, 1996).
Public, private, and protected access are purely compile-time checks,
like type checking. You can prove this by bypassing the checks, as in
the following example.
#include <iostream>
class Bypass
{
public:
typedef void (Bypass::*memberPointer)();
memberPointer expose();
private:
void privateFunction();
};
Bypass::memberPointer Bypass::expose()
{
return &Bypass::privateFunction;
}
void Bypass::privateFunction()
{
std::cout << "privateFunction\n";
}
int main(int argc, char* argv[])
{
Bypass bp;
Bypass::memberPointer mp = bp.expose();
(bp.*mp)();
char c;
std::cin >> c;
return 0;
}
If you compile and run this, you should see that the main function can
call Bypass::privateFunction, even though it is private.
This design makes sense, as it would impose an intolerable overhead if
every member function call had to include a check on whether the
caller was authorized to call the function.
Virtual functions and polymorphism, however, must have a run-time
manifestation, since the whole idea is to be able to call different
functions depending on the type of an object, which may not be known
until run time. The usual implementation design uses a virtual
function table. Any object that has virtual functions contains a
pointer to a virtual function table. (This pointer is invisible and
inaccessible to the application programmer.) The pointer to the
virtual function table corresponding to the object's type is set when
the object is constructed. When a function calls a non-virtual member
function, the compiler knows where the code for that function is and
simply generates code to call it, but when a function calls a virtual
member function, the compiler generates code to pick out a pointer to
the function from the virtual function table addressed by the pointer
in the object. Then it uses that function pointer to call the
function defined for the object's actual type.
Of course, this gets more complicated with multiple inheritance and
virtual inheritance, but compiler makers have solved these problems
too.
This is just a summary; for more details, see:
C++ FAQ answer on virtual functions (see also the following answer,
which goes into more detail)
http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.3
An MSDN Magazine answer by Paul DiLascia shows assembly language implementation
http://msdn.microsoft.com/msdnmag/issues/0300/c/
Wiki Wiki Web page on VeeTable
http://c2.com/cgi/wiki?VeeTable
To instantiate an object, the run-time code must allocate sufficient
memory for the object and run its constructor. The memory allocation
may happen at various times, depending on the object's storage class.
A static object has space allocated at compile (or link) time; an
automatic object has space typically allocated on entry to the block
in which it is defined, and a dynamic object has space allocated from
the free store at construction time.
A constructor is just a function. It gets an invisible parameter that
tells it where the storage to be initialized is.
It makes no difference if the code for the constructor is in a library
or not. When the library is linked to the main program, either
statically or dynamically, the program finds out where the constructor
is. Then it calls it the same way either way.
Additional Link
MSDN Library article "C++: Under the Hood" by Jan Gray
http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarvc/html/jangrayhood.asp
I hope this information is helpful. As I said, I can't really explain
the whole C++ object model in this space, but I have tried to provide
general answers on the points you asked about, with links to more
detailed material. If anything is not clear enough, please ask for a
clarification and I will try to improve my explanation.
Regards,
--efn |