class Foo
{
public:
void f() {}
virtual void g() {}
};
class Bar : public Foo
{
public:
void f() {}
virtual void g() {}
};
int main()
{
Foo foo;
Bar bar;
Foo *a = &bar;
Bar *b = &bar;
// case #1
foo.f();
foo.g();
// case #2
bar.f();
bar.g();
// case #3
a->f();
a->g();
// case #4
b->f();
b->g();
}
In first and second case as far as I can tell compiler should already know what types are, so I guess there be no dynamic binding.
But I'm not sure about the third and fourth cases. In third case, in my opinion compiler can know the types if it tries a little hard to guess where the pointer is pointing at. So there may or may not be dynamic binding.
In forth case, would a derived class pointer to derived class object still have to involve dynamic binding?
In general, C++ compilers are allowed to optimize aggressively so long as they follow the as-if rule. That is, they are allowed to optimize in any way as long as the program behaves as-if no optimization happened at all1 -- in terms of observable and defined behavior. (If you invoke undefined behavior then optimizations could well change behavior, but since the behavior wasn't defined to begin with, it's not an issue.)
Anyway, to get back on track, this means that a compiler may indeed compile all of the method calls in your example using static (compile-time) binding because it can prove the actual types of the objects the pointers point to, and using static binding instead of dynamic binding would not cause a change in the observable behavior of the program.
To specifically address your fourth question:
If we assume that the compiler doesn't know the type of the object being pointed to then yes, it must still involve dynamic binding if you are invoking
g()
on aBar *
. This is because it could point to an object of a class that further derives theBar
type -- maybe you introduce aclass Baz : Bar
in another compilation unit, or maybe even some third-party library loaded into your program introduces it! The compiler would have no way of knowing, so it must assume that this could happen and so it would use dynamic binding.However, if the compiler is able to prove that
Bar::g()
can't be overridden further, either because it isfinal
2 or because theBar
class isfinal
then it could use (and probably would use) static binding when invokingg()
on aBar *
.1 The standard does have specific exceptions to this rule. In particular, the compiler is allowed to elide copies of objects in some cases, which omits calls to copy constructors that would have otherwise been called. If those constructors had observable side-effects then this optimization would change the observable behavior of the program. However, the standard explicitly permits this because copies can be very expensive (think of a vector with a million elements) and because copy constructors shouldn't have side-effects anyway.
2
final
is a new keyword in C++11 that prevents a class from being inherited or prevents a virtual function from being overridden.