+3  A: 

Disregarding aspects of binding, it's not actually the compiler that determines this.

It is the C++ runtime that evaluates, via vtables and vpointers, what the derived object actually is at runtime.

I highly recommend Scott Meyer's book Effective C++ for good descriptions on how this is done.

Even covers how default parameters in a method in a derived class are ignored and any default parameters in a base class are still taken! That's binding.

Rob Wells
+19  A: 

Each object (that belongs to a class with at least one virtual function) has a pointer, called a vptr. It points to the vtbl of its actual class (which each class with virtual functions has at least one of; possibly more than one for some multiple-inheritance scenarios).

The vtbl contains a bunch of pointers, one for each virtual function. So at runtime, the code just uses the object's vptr to locate the vtbl, and from there the address of the actual overridden function.

In your specific case, Polygon, Rectangle, and Triangle each has a vtbl, each with one entry pointing to its relevant area method. Your ppoly1 will have a vptr pointing to Rectangle's vtbl, and ppoly2 similarly with Triangle's vtbl. Hope this helps!

Chris Jester-Young
vptr/vtbl. Rally I don't remember those in the standard :-) Pointer to a vtable. Where a vtable is a compiler defined structure is more descriptive.
Martin York
@Martin: vptr/vtbl are the terms used in Bjarne Stroustrup's book, The C++ Programming Language. :-)
Chris Jester-Young
I guess a vtable is not required by the standard, it just so happens that most compilers implement polymorphism using one so it has become more or less standard behaviour
1800 INFORMATION
vtable is not in the standard either. This is just one implementation of possibly many. It happens to be the one that is pretty much universally used, but it's not mandated by the standard. (out of curiosity - is there an accessible compiler out there that does something else?)
Michael Burr
I agree it's an implementation detail, but it's one I've found actually helps people understand how virtual functions are meant to work. :-)
Chris Jester-Young
Sorry not disagree with your explanation, what I was trying to say was: The lexeme 'vtbl' is not very discriptive and unless you have already heard the term vtable. When I discuss the subject with collegues we use the term vtable (nobody speaks of a vtbl).
Martin York
Pretty much all C++ and C++ like languages use vtables, I think. See here: http://en.wikipedia.org/wiki/Vtable - the section talks about languages with multiple dispatch needing something more advanced
1800 INFORMATION
The other commonly used method is a hashtable of method names
1800 INFORMATION
Ick! Not hashtable of method names! That sounds like IDispatch late binding (or whatever it's called, I'm referring to the one that looks up function pointers by method names, rather than DispIDs). Apologies if you've never played with OLE Automation and don't know what I'm talking about. :-P
Chris Jester-Young
+1  A: 

Virtual function tables.

moonshadow
+1  A: 

To answer the second part of your question: that address probably won't have a v-table in the right place, and madness will ensue. Also, it's undefined according to the standard.

Zach Snow
+5  A: 

Chris Jester-Young gives the basic answer to this question.

Wikipedia has a more in depth treatment.

If you want to know the full details for how this type of thing works (and for all type of inheritance, including multiple and virtual inheritance), one of the best resources is Stan Lippman's "Inside the C++ Object Model".

Michael Burr
+1  A: 
cout << ((Polygon *)0x12345678)->area() << endl;

This code is a disaster waiting to happen. The compiler will compile it all right but when it comes to run time, you will not be pointing to a valid v-table and if you are lucky the program will just crash.

In C++, you shouldn't use old C-style casts like this, you should use dynamic_cast like so:

Polygon *obj = dynamic_cast<Polygon *>(0x12345678)->area();
ASSERT(obj != NULL);

cout << obj->area() << endl;

dynamic_cast will return NULL if the given pointer is not a valid Polygon object so it will be trapped by the ASSERT.

Adam Pierce
You can't dynamic_cast from an integer! (In fact you can't dynamic_cast from void* either, you have to start from a pointer/reference of a type that has some relation to the type you're casting to.)
Chris Jester-Young
So my point is that with a random address like reinterpret_cast<void*>(0x12345678) you're in undefined behaviour zone no matter what. :-P
Chris Jester-Young
I've actually done this kind of thing to store object pointers in a Windows list box. Admittedly I have to write it like this: MYTYPE *obj = dynamic_cast<MYTYPE *>((MYTYPE *)listbox.GetItemData(item)); The dynamic_cast is a bit safer than a straight cast.
Adam Pierce
If GetItemData returns a void*, you can avoid C-style casts by using a static_cast<MYTYPE*> instead. :-) Some people I know are quite dogmatic about avoiding C-style casts, because they can be quite a blunt tool (can cause reinterpret_cast in unintended cases, for example).
Chris Jester-Young
On the other hand, if GetItemData returns an int, then reinterpret_casting it to a pointer is not 64-bit safe, in which case I'd probably use a map<int, MYTYPE*>. :-)
Chris Jester-Young
All good advice. I see you really know your stuff Chris.
Adam Pierce
+1  A: 

Virtual function tables. To wit, both of your Polygon-derived objects have a virtual function table that contains function pointers to the implementations of all their (non-static) functions; and when you instantiate a Triangle, the virtual function pointer for the area() function points to the Triangle::area() function; when you instantiate a Rectangle, the area() function points to the Rectangle::area() function. Because virtual function pointers are stored along with the data for an object in memory, every time you reference that object as a Polygon, the appropriate area() for that object will be used.

McWafflestix