Defaul parameter values are statically bound, while virtual function - the only functions we should be overriding - are dynamically bound.
As item 36 suggests, we should never redefine an inherited non-virtual function, so we only need to focus on the case where we inherit a virtual function with a default parameter value.
To make things clear, first we need to understand the difference between static and dynamic binding:
- an object’s static type is the type we declare it to have in the program text
- an object’s dynamic type is determined by the type of the object to which it currently refers.
For example:
|
|
|
|
Notice that the static type of ps
, pr
, and pr
are all “pointer-to-Shape
”, and that it makes no difference what they’re really pointing to.
Dynamic types , as their name suggests, can change as a program runs, typically through assignments:
|
|
Virtual functions are dynamically bound, meaning that the particular function called is determined by the dynamic type of the object through which it’s invoked:
|
|
However, since default parameters are statically bound, when calling a virtual function defined in a derived class, it is possible that the default parameter value we are using is from a base class:
|
|
In this case, even though the virtual function is called according to dynamic type Rectangle*
, the default parameter value is taken from the Shape
class (Shape::Red
) rather than the derived Rectangle
class (Shape::Green
is expected). This result is almost certainly unanticipated.
The problem here is that the default parameter value of virtual function draw
is redefined in a derived class. This inconsistency between the function call’s dynamic binding and the default parameter value’s statical binding is mainly due to C++’s insistence on runtime efficiency.
When we’re having trouble making a virtual function behave the way we’d like, it’s wise to consider alternative designs, which item 35 happens to offer. One of the alternatives is the non-virtual-interface idiom:
in order to make clear that the default value for draw
’s color parameter should always be Red
, we could declare a public non-virtual function in the base class calling a private virtual function that derived classes may redefine. According to item 36, such an non-virtual function should never be overridden by derived classes. Thus, simply specify the default parameter in the non-virtual function, and we’re done:
|
|