Objects are different
It’s important to realize that upcasting deals only with addresses. If the compiler has an object, it
knows the exact type and therefore (in C++) will not use late binding for any
function calls – or at least, the compiler doesn’t need to use late
binding. For efficiency’s sake, most compilers will perform early binding when they are making a call to a
virtual function for an object because they know the exact type. Here’s an
example:
//: C15:Early.cpp
// Early binding & virtual functions
#include <iostream>
#include <string>
using namespace std;
class Pet {public:
virtual string speak() const { return ""; }};
class Dog : public Pet {public:
string speak() const { return "Bark!"; }};
int main() { Dog ralph;
Pet* p1 = &ralph;
Pet& p2 = ralph;
Pet p3;
// Late binding for both:
cout << "p1->speak() = " << p1->speak() <<endl;
cout << "p2.speak() = " << p2.speak() << endl;
// Early binding (probably):
cout << "p3.speak() = " << p3.speak() << endl;
} ///:~
In p1–>speak( ) and p2.speak( ),
addresses are used, which means the information is incomplete: p1 and p2
can represent the address of a Pet or something derived from Pet,
so the virtual mechanism must be used. When calling p3.speak( )
there’s no ambiguity. The compiler knows the exact type and that it’s an
object, so it can’t possibly be an object derived from Pet – it’s exactly
a Pet. Thus, early binding is probably used. However, if the compiler
doesn’t want to work so hard, it can still use late binding and the same
behavior will occur.