Since the inherited non-virtual function is incorrectly redefined (see the previous clause), this clause limits the problem: never redefine a virtual function that inherits the default parameter value.
(1)
Virtual functions are dynamically bound, but the default parameter is static binding.
The so-called static type of an object is the type it uses when declared in a program.
You may call a virtual function defined in derived class while using base class as the default parameter value.
(2)
Why cannot the default parameter values of the inherited virtual functions be redefined?
In fact, the reason is quite simple: the default parameter is static binding, while the virtual function is dynamic binding. static binding of an object is also called pre-binding. It indicates that the object type and behavior can be determined during program compilation. For example:
Class Shape {public: enum Color {RED, GREEN, BLUE}; virtual void draw (Color color = RED) const = 0 ;...}; class Circle: public Shape {public: // Oh, Europe! Actually changed the default parameter value virtual void draw (Color color = GREEN) const {...}}; class Rectangle: public Shape {public: // you do not need to specify the parameter type. You need to explicitly specify the value of this parameter. // The default value of the inherited base class is not bound statically, if it is called with a pointer or reference, you do not need to specify the default value, because dynamic binding // inherits the default value of the base class parameter virtual void draw (Color color) const {...}};
Let's take a look at the following pointers:
Shape* ps; Shape* pc = new Circle; Shape* pr = new Rectangle;
Ps, pc, and pr here, no matter what object it points to, their static type is Shape *. Dynamic types are the types of objects they actually point. Therefore, the dynamic type of pc is Circle *, and the dynamic type of pr is Rectangle *. ps does not point to any object, so there is no dynamic type at this time.
(3) read the following statement!
Pc-> draw (); // note the following: Circle: draw (RED)
How can we call Circle: draw (RED !? Why not Circle: draw (GREEN )?
Cause:
(1) based on the fact that the pointer is used in the call statement, we know that the called version should be the dynamic function version of the pointer, that is, Circle: draw, this problem is not big.
(2) Let's take a look at its value passing parameter. we mentioned earlier that the default parameter value is statically bound, while the static type of pc is Shape *, therefore, the input value of this parameter is the default value of this function version of Shape.
So why does C ++ stick to this well-behaved approach? The answer is the efficiency during the runtime. If the default value is also dynamically bound, you must have a way to determine the appropriate parameter default value for the virtual function during the runtime. if this is done, it will be slower and more complex than the current implementation of the "Decision during compilation" mechanism. Considering the execution speed and implementation simplicity, C ++ gave up this practice.
(4) Solution!
Now, in order to follow this Convention but provide the default parameter value to your base class and parent class at the same time, the Code is like this:
class Shape{ public: enum Color{RED,GREEN,BLUE}; virtual void draw(Color color = RED)const = 0; ... }; class Circle:public Shape{ public: virtual void draw(Color color = RED)const {...} };
Obviously, the code is repeated! Besides, if you want to change the default value, you must change the default value of the base class and sub-class functions at the same time. If you are not careful, errors may occur, resulting in unexpected errors. is there a more convenient way of writing? Of course, do you still remember NVI? (Non-virtual interface). If you forget it, let's look back at Clause 35. In this way, we will write the following code:
class Shape{ public: enum Color{RED,GREEN,BLUE}; void draw(Color color = RED) const{ ... doDraw(color); ... } ... private: virtual void doDraw(Color color) const = 0; }; class Circle:public Shape{ ... private: virtual void doDraw(Color color){ ... } };
Since draw is non-virtual and non-virtual is never rewritten (Clause 36), the default color value is always RED.
Remember:
(1) Never redefine an inherited default parameter value, because the default parameter values are static bindings, while the virtual function-the only thing you should override-is dynamic binding.