Which functions are dynamically dispatched?
I've searched for questions, looking at forums, books, etc. I can recognize a polymorphic behavior of methods, and there are lots of simple examples when an invoked method is decided in compile or runtime. But I was confused by this code, where a class C inherits from B that inherits from A:
class A {
protected:
int x;
public:
virtual void change() = 0;
virtual void change(int a) { x = a; }
};
class B : public A {
public:
void change() { x = 1; }
};
class C : public B {
public:
void change() { x = 2; }
void change(int a) { x = a*2; }
};
int main () {
B *objb = new B();
C *objc = new C();
A *obja;
objb->change();
obja = objc;
objc->change();
obja->change(5);
// ...
}
Many examples tells (and it is clear) that a polymorphic behavior occurs and it is decided in runtime what method to call when the following line is executed:
obja->change(5);
But my questions are:
What happens when I call the following (overrid开发者_如何学Pythoned from a pure virtual)?
objb->change();
What happens when I call the following (overrided from a virtual, but non-pure)?
objc->change(5);
Since the class declaration of the pointer variables are the same of the objects, should the method calling be decided at compile or runtime?
If the compiler can deduce the actual type at compile time, it can avoid the virtual function dispatch. But it can only do this when it can prove that the behavior is equivalent to a run-time dispatch. Whether this happens depends on how smart your particular compiler is.
The real question is, why do you care? You obviously understand the rules for calling virtual functions, and the semantics of the behavior are always those of a run-time dispatch, so it should make no difference to how you write your code.
There are three issues to consider. The first is overload resolution: in this case, the compiler uses the static type of the expression to construct the set of functions it chooses from. Thus, if you had written:
objb->change( 2 );
the code wouldn't have compiled, because there is no change
which
takes an int
in the scope of B
. Had there been no change
at all
in the scope of B
, the compiler would have looked further, and found
the change
(all of them) in A
, but once it finds the name, it stops.
This is name lookup and function overload resolution, and it is entirely static.
The second issue is which function should be called, once the compiler has chosen to call a specific function in the interface. If the chosen function is virtual, the actual function called will be the function with the exact same signature in the most derived class of the dynamic type—that is, the type of the actual object in question.
Finally, there is the question of whether dynamic dispatch is used in the generated code. And that's entirely up to the compiler. The compiler can do anything it wants, as long as the correct function, as determined by the two preceding issues, is called. Generally: if the function isn't virtual, dynamic dispatch will never be used; and if the access is directly to the object (named object or temporary), dynamic dispatch will generally not be used, since the compiler can trivially know the most derived type. When the call is through a reference or a pointer, the compiler will generally use dynamic dispatch, but it is sometimes possible for the compiler to track the pointer enough to know the type it will point to at runtime, and forego dynamic dispatch. And good compilers will often go further, using profiler information, to determine that 99% of the time, the same function will be called, and the call is in a tight loop, and will generate two versions of the loop, one with dynamic dispatch, and one with the most frequently called function inlined, and select which version of the loop via an if, at runtime.
objb->change()
calls B::change()
because objb
contains address of an object of type B
objc->change(5);
calls C::change(int)
because objc
contains address of an object of type C
The method calling will be still dynamic/run time because the methods B::change()
& C::change(int)
are still virtual, because the virtual attribute in inherited.
To answer the question of whether the functions are dynamically dispatched or at compile time
The answer is No can definitely say whether it will be a compile time dispatch or dynamic dispatch. The dynamic/run time dispatch takes place in the first place because the compiler cannot definitely decide which versions of the functions to call at compile time, So if a compiler can deduce a definite manner as to which function to call, the dispatch might very well be decided at compile time itself.
Having said so, whether the dispatch happens at run time or compile time does not change the semantics of calling which version of overidden function gets finally called because the C++ standard explicitly states the rules of which functions of functions in this regard.
精彩评论