Virtual FunctionsThe function is to implement dynamic association, that is, to dynamically select an appropriate member function in the running stage of the program. After defining a virtual function, you can redefine the virtual function in the derived class of the base class. The newly defined function in the derived class should have the same number and type as the virtual function.
1. Do not add virtual functions if necessary
For example, we have the following hierarchical design of the ball class, in which we need to determine whether a certain ball can be played (kickable ):
- class Ball
- {
- public:
- virtual bool IsKickable() = 0;
- };
- class Football
- {
- public:
- virtual bool IsKickable() {return true;}
- };
- class Basketball
- {
- public:
- virtual bool IsKickable() {return false;}
- };
At first glance, I think it is quite reasonable, but think about it carefully. In fact, IsKickable is an essential static attribute of a certain ball. It is a waste to use a virtual function to express this information, A more reasonable approach should be to use a data member and a common member function:
- class Ball
- {
- public:
- bool IsKickable(){return m_bIsKickable;}
- protected:
- bool m_bIsKickable;
- };
- class Football
- {
- public:
- Football():bIsKickable(true){}
- };
- class Basketball
- {
- public:
- Basketball():bIsKickable(false){}
- };
I have met such a design for at least two times, one for review and the other for review. The result is changed to the second method that we think is more reasonable.
2. Do not use "|" for complicated logic judgment
"|" Is a "or operation" symbol. It is very simple and clear when you actually use it as an OR operation. But someone has invented a tricky method to use it.
For example, our program may have three States: A, B, or C. Now there is A variable bOk. If the current state of the program is C, the bOk must be true, how to come to assert? The following is an intuitive method:
- if(IsC()) assert(bOk);
However, some people think that an if statement is troublesome, so they invented:
- assert(IsA() || IsB() || bOk);
The logic is as follows: if it is neither A nor B, then bOk must be true. Although the Code is simplified to only one statement, this poses a challenge to understanding.
We generally do not recommend this inintuitive Method for judgment.
Iii. Pure virtual functions and default implementations
There is a base class. We expect it to be an abstract class, but we also expect its virtual functions to be implemented by default. This is actually a syntax problem: we can set a virtual function as pure virtual and provide default implementation. But at first I thought it would not work. I wanted to set the constructor as pretected to achieve a similar effect, but it was not very reasonable in terms of concept)
In this case, I don't think it is necessary to set all functions as pure virtual, and find a typical example, such as setting the Destructor as pure virtual and providing the default implementation:
- class Base
- {
- public:
- virtual ~Base() = 0;
- };
- Base::~Base() {printf("~Base()\n");}
- class Derive: public Base
- {
- public:
- virtual ~Derive(){printf("~Derive()\n");}
- }
In this way, the base class is already an abstract class and should be an acceptable solution.