GURU of the week # 5: Redefinition of virtual functions

zhaozj2021-02-08  252

Author: Hub Sutter Translator: plpliuly

/ * This article is the 5th article of the Gotw (GURU of the Week) series of self-entertainment translations, the original copyright is the author of Hub Sutter (the famous C expert, "Exceptional C "). The translation of this article did not have the consent of the original author, only for learning discussion. - Translator * /

# 5 Reproduction of virtual functions (proposed on May 14, 1997) Difficulty: 6/10

The virtual function is a basic feature of C , isn't it right? If you can answer the following questions, then you have already mastered.

Question: Suppose you are looking at some very old source code, some of which are as follows, the author doesn't know his people. This author writes this code like just to verify the working mechanism of C features. The author is expected. What kind of results are printed in this program, what is the actual output result? #Include #include Using Namespace Std;

Class base {public: Virtual void f (int) {cout << "Base :: f (int) << end1;

Virtual void f (double) {cout << "Base :: f (double) << endl;}

Virtual Void G (INT i = 10) {cout << i << endl;}};

Class Derived: public base {public: void f (complex ) {cout << "derived :: f (complex) << endl;

Void G (INT i = 20) {cout << "Derived :: g ()" << i << i e;

Void main () {base b; derived d; base * pb = new deerive;

B.F (1.0); D.F (1.0); PB-> F (1.0);

B.G (); D.G (); PB-> g ();

Delete Pb;}

answer:

First, some code style issues: 1.void main () This is not a legitimate statement of a Main function, although many compilers allow this. It should be used "int main ()" or "int main (int Argc, char *) Argv []) ". However, please note that there is no need to return the result (although the return value for returning an error to the outside is a good style of writing functions) ... if the main does not return the statement, the effect is "Return 0;".

2. Delete Pb;

This seems that there is nothing wrong, but only when the base class provides a virtual destructor function. Otherwise, through a pointer to the base class without a virtual destructor, it is dangerous, because The result you can get is wrong. (Translator Note: At least a memory leak) [Principle] Define the destructor of the base class as a virtual function

3.Derived :: f (Complex )

Derived class did not overload Base :: f ... it only covers them. The difference here is very important, because this means base :: f (int) and base :: f (double) to Derived Category is not visible! (Note: Some popular compilers do not even give this even warning error.) [Principles] When you define a function that is the same as the function in the base class in the inheritance class, if you don't want to The function in the base class is hidden, you must use the "using" instruction statement to incorporate the functions in the base class into the SCOPE of the inheritance class, so that the inheritance class can be seen. 4.Derived :: g (int i = 10)

Unless you really want others to confuse, don't change the parameter default value of the function in inheritance class. (Generally, try to use function overload to replace parameters default change is a good method.) Of course, This is a legal C statement, the result is also presented, but don't do this. You can see how this will make others confused in the discussion. [Principle] Never change the parameters of the function in the base class value.

After discussing several major code style problems, let us cut into the topic to see if the program will run as the code author wants:

Void main () {base b; derived d; base * pb = new deerive;

BF (1.0); no problem. Call Base :: f (double). DF (1.0); call Derived :: F (Complex ). Why? Remember Derived Class No Declaration "Using Base :: F ", So Base :: F (int) and base :: f (double) will not be called. The code author may wish to call the latter, but even a compilation error will not give .comLeth A implicit conversion (*) for the double type, so the compiler will see the above statement as Derived :: F (COMPLEX (1.0)). (*) According to the current C standard draft, this conversion constructor Not Explicit

Pb-> f (1.0); interesting is, although Base * Pb points to a DeriveD object, this is called Base :: F (Double) because the compiler's overloaded interpretation is based on static data Type (here is BASE) instead of dynamic type (which is derived). BG (); this statement will print "10" because it calls Base :: g (int), the default value of its parameters It is 10. No doubt.

D.g (); This statement will print "Derived :: G () 20" because it calls Derived :: G (int), the default value of its parameters is 20. There is also no question.

PB-> g (); this statement will print "Derived :: g () 10" ... This may make you scared a big jump, Best. But the compiler is done is right. Need to remember Yes, like the function overload, the parameter default is interpreted from the statically type of the object (here is BASE), thus the parameter defaults to 10. However, the function here happens to be a virtual function, Therefore, the actual function is determined by the dynamic type of the object (which is Derived). If you fully understand the last few explanations above, you will understand the problem we discussed today. Congratory you. Delete PB; } Delete here will apparently let the object only partially release, causing memory errors ... Take a look at the virtual constructor discussed above. ------ (end)

转载请注明原文地址:https://www.9cbs.com/read-1037.html

New Post(0)