Valid C ++ distinguishes between interface inheritance and implementation inheritance, and valid Interfaces
Clause 34: differentiate interface inheritance and implement inheritance
The content in this article is too long, but its thoughts are not complicated. Only three sentences can be understood. The first sentence is: pure virtual functions inherit only interfaces; the second sentence is: virtual functions inherit both interfaces and provide a default implementation; the third sentence is: ordinary functions inherit both interfaces and implement them forcibly. It is assumed that all the member functions discussed are public.
Here we will review the three types of functions as follows:
Class BaseClass {public: void virtual PureVirtualFunction () = 0; // void virtual ImpureVirtualFunction (); // void CommonFunciton (); // common function };
A pure virtual function has a declaration of "equal to 0". The specific implementation is generally placed in the derivation (but the base class can also have a specific implementation). The class (called the virtual base class) the object cannot be defined. This pure virtual function can still not be implemented in the derived class, which is implemented by the derived class. In short, it is implemented by a derived class, can be defined by this derived class.
Virtual functions must be implemented. Otherwise, a link error is reported. Virtual functions can provide different versions in the base class and multiple Derived classes, when the program runs, it dynamically determines which version of the virtual function to execute (the mechanism is the virtual table generated by the compiler ). The virtual keyword must be explicitly specified in the base class. It is not required in the derived class. Even if it is not written, it will be recognized as a virtual function by the compiler. Classes of virtual functions can define instance objects.
Normal Functions inherit both interfaces and implementations. If a common function is redefined in a derived class, the name will be overwritten (see clause 33). In fact, it is also not recommended to overwrite common functions of the base class in the derived class. If you really want to do this, consider whether to declare the function of the base class as a virtual function or pure virtual function.
The following are three types of member functions:
Class BaseClass {public: void virtual PureVirtualFunction () = 0; // void virtual ImpureVirtualFunction (); // void CommonFunciton (); // common function }; void BaseClass: PureVirtualFunction () {cout <"Base PureVirtualFunction" <endl;} void BaseClass: ImpureVirtualFunction () {cout <"Base ImpureVirtualFunciton" <endl ;} class DerivedClass1: public BaseClass {void PureVirtualFunction () {cout <"DerivedClass1 PureVirturalFunction Called" <endl ;}; class DerivedClass2: public BaseClass {void callback () {cout <"DerivedClass2 PureVirturalFunction Called" <endl ;}; int main () {BaseClass * b1 = new DerivedClass1 (); BaseClass * b2 = new DerivedClass2 (); b1-> PureVirtualFunction (); // call PureVirtualFunction b2-> PureVirtualFunction () of DerivedClass1; // call the analysis of PureVirtualFunction b1-> BaseClass :: pureVirtualFunction (); // Of course, you can also call the PureVirtualFucntion return 0 of the BaseClass version ;}
The book advocates replacing virtual functions with pure virtual functions, because virtual functions provide a default implementation. If the behavior of the derived class is different from that of the virtual function, however, if you forget to overwrite the virtual function, the problem may occur. But the pure virtual function does not, because it restricts from the syntax that the derived class must implement it, otherwise it will not be able to define the object of the derived class.
At the same time, because pure virtual functions can also be implemented by default (but it emphasizes from the syntax that the derived class must be redefined, otherwise the object cannot be defined), it is completely possible to replace virtual functions.
The meaning of a common function is immutable overhead and specicity. Therefore, it should never be redefined in a derived class.
When designing a class member function, do not declare all functions as non-virtual (common functions). This will enable extra space for special work; generally, do not declare all functions as virtual functions (virtual functions or pure virtual functions), because some member functions can be determined by the base class, shared by all derived classes. This design rule is not absolute and depends on the actual situation.
Summary:
Interface inheritance and implementation inheritance are different. Under public inheritance, derived class always inherits the interface of base class;
The pure virtual function only specifies the interface inheritance;
The impure virtual function specifies the interface inheritance and default implementation inheritance;
The non-virutal function specifies the interface inheritance and mandatory implementation inheritance.
Article 35: consider other options than virtual functions #
For example, consider an application example of a virtual function:
Class GameCharacter {private: int BaseHealth; public: virtual int GetHealthValue () const // returns the blood volume of game characters {return BaseHealth;} int GetBaseHealth () const {return BaseHealth ;}}; class KnightBoss: public GameCharacter {public: virtual int GetHealthValue () const {return GetBaseHealth () * 2 ;}}; int main () {GameCharacter * CommonCharacter = new GameCharacter (100 ); gameCharacter * Boss = new KnightBoss (100); cout <"CommonCharacter heath =" <CommonCharacter-> GetHealthValue () <endl; // return 100 cout <"KnightBoss heath =" <Boss-> GetHealthValue () <endl; // return 200}
GetHealthValue gets the corresponding blood volume based on different types of game roles. But here, the virtual function is public, and a genre of NVI (Non-Virutal Interface) advocates that all virtual functions are private, separate the pre-and post-methods used by both the parent and child classes as a non-virtual function, as shown below:
Class GameCharacter {private: int BaseHealth; public: GameCharacter (int bh = 100): BaseHealth (bh) {}int GetBaseHealth () const {return BaseHealth;} int GetHealthValue () {cout <"may be some pre-check" <endl; int Result = DoGetHealthValue (); cout <"may be some post-processing" <endl; return Result ;} private: virtual int DoGetHealthValue () const // returns the blood volume of game characters {return BaseHealth ;}}; class KnightBoss: public GameCharacter {public: KnightBoss (int bh): GameCharacter (bh) {} private: virtual int DoGetHealthValue () const {return GetBaseHealth () * 2 ;}};
The interface of the main function does not need to be changed. Here we write the methods specific to the parent class and subclass in their respective private ranges. The advantage of this is that you can do something well done (as shown in the cout method in the Program). The former method can be to lock the mutex, print logs, and verify the validity of input data, the solution is to unlock the lock and verify the validity of the data after the event.
Note that all the virtual functions are set to private, but the virtual table pointer generated by the compiler is not private. Otherwise, the private member variables cannot be inherited to realize polymorphism. NVI is not an absolute method. For example, a virtual destructor must be public to ensure that its subclass and its subclass can smoothly release resources.
Are there other methods to replace virtual functions? In essence, virtual functions are controlled by virtual pointers and virtual tables. virtual pointers point to a function entry address in the virtual table, which achieves polymorphism. Therefore, we can use a virtual pointer to point to a function as a function pointer. As shown below:
class GameCharacter{private: int BaseHealth;public: typedef int(*HealthCalcFunc)(const GameCharacter&); GameCharacter(int bh = 100, HealthCalcFunc Func= NULL) :BaseHealth(bh), HealthFuncPoniter(Func){} int GetHealthValue() { if (HealthFuncPoniter) { return HealthFuncPoniter(*this); } else { return 0; } } int GetBaseHealth() const { return BaseHealth; }private: HealthCalcFunc HealthFuncPoniter;};class KnightBoss : public GameCharacter{public: KnightBoss(int bh, HealthCalcFunc Func) :GameCharacter(bh, Func){}private:};int HealthCalcForCommonMonster(const GameCharacter& gc){ return gc.GetBaseHealth();}int HealthCalcForKnightBoss(const GameCharacter& gc){ return 2 * gc.GetBaseHealth();}
The content of the main function still does not need to be changed and the results are the same. In fact, here we just use the function pointer to simulate the virtual table pointer. The form of this function pointer is declared in the parent class. It returns an int value, but because the method in GameCharacter needs to be used, the form parameter is referenced by GameCharater. The parent class has a private member variable, which is a function pointer that can point to a specific function. You can add different functions to the constructor of the parent class and subclass to call the GetHealthValue of the parent class.
The above uses typedef to declare the function pointer type, and then defines the function that participates in the return value of the specified shape. During the construction, the function pointer in the class points to a specific function, then we will think, can we use classes instead of typedef? See the following example:
Class HealthCalcFunctionBaseClass {public: virtual int CalcHealth (int BaseValue) // The book uses const GameCharacter &, and the class GameCharacter is declared before, but the specific method of GameCharacter is used, therefore, the compiler reports an error. I don't know how you solve this problem. {return BaseValue ;}}; class HealthCalcFunctionDerivedClass: public HealthCalcFunctionBaseClass {public: virtual int CalcHealth (int BaseValue) {return 2 * BaseValue ;}}; class GameCharacter {private: int BaseHealth; public: GameCharacter (int bh = 100, HealthCalcFunctionBaseClass * Func = NULL): BaseHealth (bh ), healthFuncPoniter (Func) {}int GetHealthValue () {if (HealthFuncPoniter) {return HealthFuncPoniter-> CalcHealth (GetBaseHealth ();} return 0;} int GetBaseHealth () const {return BaseHealth;} private: HealthCalcFunctionBaseClass * HealthFuncPoniter;}; class KnightBoss: public GameCharacter {public: KnightBoss (int bh, HealthCalcFunctionBaseClass * Func): GameCharacter (bh, Func) {}}; // int main () {comment * CommonMonsterCalc = new HealthCalcFunctionBaseClass (); Comment * BossCalc = new HealthCalcFunctionDerivedClass (); comment * CommonCharacter = new comment (100, comment); GameCharacter * Boss = new KnightBoss (100, BossCalc); cout <"CommonCharacter heath =" <CommonCharacter-> GetHealthValue () <endl; // returns 100 cout <"KnightBoss heath =" <Boss-> GetHealthValue () <endl; // returns 200}
Some readers will say that since we are discussing whether to use other methods to replace virtual functions, here we are still using a class with virtual functions. Indeed, this is just an implementation method of the author's application of the strategy mode. The first NVI method itself is inseparable from the private virutal function. If you think it is contradictory, then we should use the pure function pointer method. (The author may want to use a special virtual form to replace the virtual function we usually see)
The book also mentions the replacement method of the imitation function. If you are interested, please take a look. The following is a summary:
Alternative to virtual functions, NVI methods and multiple forms of Strategy design patterns. The NVI Method itself is a special form of Template Method design mode;
One disadvantage of transferring a function from a member function to a class external function is that a non-public member of the class cannot be accessed by a non-member function.