Whether the virtual function should be declared only for private / protected?
Import
I want everyone to speaking, virtual functions can't be a strange concept. As for how to use it, most people will tell me that by rewriting the virtual function in the OVERRIDE base class, Polymorphism. Nice, the role of virtual functions is just the case. But if I want you to say that the virtual function is declared as public and declared as the difference between private / protected, do you still tell me about the answer as before?
In fact, I like to declare the virtual function as public (I didn't do too much investigation, because the programmers around me did this.). The benefits of doing this are obvious: we can easily at the client (Client, relative to server, server refers to the inheritance architecture we used, the client refers to the external code of the method / function in the system) calls it. Not through the use of those annoying USING declarations, or impose the Friend relationship to the class to meet the compiler's Access requirements. OK, this is a very good practice, simple, and can achieve our requirements.
However, according to another feature in the three major characteristics of OO (Encapsulation) (the other is inherited), we need to separate the interface (IMPLEMENTATION), ie, an outward manifest as a universal interface. And put the on-the-art details in the module and do not let Client end know. The interface is separated from the actual, so that we can design a system with lower coupling, better scalability, and can also extract more reusable (Reusable) from such a system.
For OO, the package is its head and other major events, with the highest rights, and other designs have conflicts with it, it is based on its design. In this way, the problem will come out, in case the polymorphics we want to appear, it is exactly the specific actual details and we don't want to expose it to the Client end. How should we change our design to make it adapt to packaging What is the need?
Feasible solution
Fortunately, C not only supports public virtual functions, but also a private / protected virtual function. (Here I don't want to say more about public and private / protected.) The former is our common form, and I don't say much, we are mainly concerned about the virtual function of Private / Protected.
You may have doubts, since the virtual function is declared as private (protected is not counted, because the subclasses can directly access the base class Protected member), how can it rewrite it in the subclass? Here, your doubts are redundant, C standard (also known as ISO 14882) tells us that there is no relationship with the virtual function of the virtual function, even if it is a virtual function declares that it is a virtual function of Private, in the subclass It can also rewrite it. Therefore, encountering the problems mentioned above, we can get the following design:
Class base {
PUBLIC:
Void do_something ()
{
// ......
Really_do_something ();
// ......
}
Private:
Virtual void real_do_something ()
{
// do the polymorphism code here
}
}
Class Derived: public base {
Private:
Void real_do_something ()
{
// do the polymorphism code here
}
}
If we need to get the actual polymorphism from the above design, you can call do_something as if you are below:
// Client Code
Base & b; // or base * pb;
B.DO_SOMETHING (); // or pb-> do_something ();
This way we can solve the problem that is presented in the beginning.
Problem
Is that ended this? No. On the contrary, we have begun to conduct our discussion today. Let us first take a look at the realization of polymorphism:
Void Base :: do_something ()
{
// ......
Really_do_something ();
// ......
}
We can find that after calling real_do_something () that is really contributing to the polymorphic contribution, we can add our own code (such as some control code, etc.), so that we "seem" can easily realize it. "Design By Contract" (DBC) "proposed by Bertrand Meyers:
Void Base :: do_something ()
{
// ur precondition code here
Really_do_something ();
// outcondition code here
}
Then, let's take a look at the Template Method this pattern [2], and find that the so-called template method is mainly carried out in this way. So, can we think so? State all the virtual functions as private / protected, then use a public non-virtual function to call it so that we don't get all the benefits listed above?
Detailed analysis
Simple, it seems that it is really good to make the benefits, neither the efficiency loss (by using the public non-virtual function inline, simple function transfer overhead can be eliminated), and Get all the benefits of the above. Why not do it?
In fact, many programmers are doing this (the results of Herb Sutter survey show that this even includes programmers, of course, of course, what they consider The reason why the skill will not be just the reasons for others given below ^ _ ^). Some people even think that the virtual function should be declared as private / protected (of course, the virtual destructor cannot be counted in it, otherwise there will be a big chaos).
But let us carefully think about it, think about some of the more extreme examples. Suppose we have a class, it has a very large number of virtual functions (even more than 10,000), even if most of the simple function transfer movements, should we still pay every virtual function for it? Both provide an open non-virtual interface? At this time, provide an interface class for your program (ie noble variables, all methods are classes of pure virtual functions) are a good solution.
Also, because the results of this will be: The non-virtual interface function of the public class in the base class must be able to fit all the subclasses, so that we will push all the responsibilities to the base class, which cannot be considered A good design method. Suppose we have a deep architecture of the inheritance system. After many inherits for the base class, we suddenly found that new subclasses have not adapted to the original interface. So, in order to continue to implement our virtual function private, we will have to turn out the code of the base class and correct it. Fortunately, the code of the base class is what we can get so that we can do the least or the opportunity to correct (although sometimes we can't understand the code in the base class); bad, our base The class is obtained in a library we use, and the code we can't get, this time should we do? It can be seen that if the design may be inherited in the class inheritance system architecture that may be inherited in the design, the requirements for designing the base class will change very high (because after in the future The consequences of any small changes caused by the class will be significantly enlarged when they pass to the low end of the inheritance), and the designer will guess all possible use of all possible use is unrealistic, which is easy to produce fragile Need to be a frequently modified design. Keep in mind: FBC (FRAGILE BASE CLASS) is a terrible thing, and this should be avoided in our procedure. In addition, do you have a good reason before you decide to change the virtual function in your program to private / protected? If you just say: "Oh, I don't know, but doing this can play a certain day later." Yes, always keep scalability is a good thing, but it is based on what you can foresee the future extension (this foresee is mainly from your deep understanding of the field or you. Usual experience). In the case where there is no reason, only one sentence "it may be useful", "I will add it in my own program, it seems that it seems to be very dazzling, but in fact it may have hundreds of damage to your procedure. Benefit. In all of our existing Framework, there are many features that "may use it later", and the results are ultimately proven to have not been used, which can't be said to be a waste of development. Therefore, let us remember the YNGNI (You Never Going to Need IT) mentioned in XP [3], and do not provide it for the features that are not used in this stage. However, if you can foresee the future extension, please leave an extensible convenience for it.
In addition, based on the compiler's perspective, when you change the base class, then the need to recompile is not only the base class itself, but all the derived classes from the base class will be recompiled. In this way, we have to waste a lot of compilation time. Especially when we decide to transfer a lot of inline to transfer, the required time is more (because the inline function will be extended into the actual call code when compiling). This can also be considered a syntax FBC issue. In addition, when you decide to add a function to your inheritance system and change the behavior of the base interface, you may have destroyed the entire inheritance system, and the external Client end code has also been shocked. This situation can be regarded as a semantic FBC issue. Remember: Stable code will never build on the unstable code.
Now let's go back to Template Method. When should I use TM? Get it from Design Patterns: Define the skeleton of an algorithm in an operation, and delay some steps into the subclass. Template Method allows subclasses to redefine certain specific steps of the algorithm without changing the structure of an algorithm. This is the virtual function we talked to private / protected, which we will use private / protected virtual functions when implementing TM, but not all private / protected virtual is TM. Finally, there is also a problem with private / protected virtual to: OO. We know that the elasticity in OO is usually provided by the polymorphism in the inheritance, but sometimes we will also use the entrustment in the combination. There is actually a lot of patterns doing this, such as: Proxy, Adapter, Bridge, Decorator, etc. If you pursue Private / Protected Virtual, it is necessary to make us only inherit in the program. For a tree, give up a forest, I think everyone is not willing to do it.
in conclusion
Half a day, I should also hire :-) Now start my point of view:
Generally speaking, the virtual function declares that private / protected is a very good design method [4], but if it is used as a unique sliver bullet, there will be many problems. In this article, I just talk about some of them, and some of the content is not fully organized because it is still not fully organized today. I hope to be able to complete it next time.
Reference
1, Object-Oriented Software Construction, Second Edition, Bertrand Meyer, Tsinghua University Press (Copyright)
2, design mode can be used for object-oriented software, GOF, Li Hongjun and other translations, machinery industry publishing house
3, Conversations: Virtually Yours, Herb Sutter & Jim Hyslop, Cuj
As well as information related to the network
postscript
The initial impulse of this article is derived from newsgroup: Comp.lang.c . Moderated one discussion: Virtual Methods SHOULD IonLY BE PRITUAL METHODS SUTERD ONLY BE PROTECTED? After watching KEVLIN Henney, Herb Sutter, and James Kanze after the wonderful remarks I always want to write my feelings. At the beginning, I wrote a lot, but I didn't finish writing. Recently, due to the busy situation, it will slowly give this matter. Not a worm, I want to say, I don't know when I can give it to you :-(, even now, because I haven't reviewed this information for a long time, many things have not been written, If you feel that you are still unfair, you can find the Thread directly to NewsGroup, with a complete discussion.
[1] "Object-Oriented Software Construction" Chapter 11: Design By Contract: Building Reliable Software, the book is available in the country.
[2] "Design Patterns: Elements of Reusable Object-Oriented Software", China has a Chinese translation of the book [3] Extreme Programming, a lightweight software development method, focusing on the flexibility, testing of the development Other ... You can get more information about it from the website: www.extremeprogramming.org
[4] You can see the Conversations: Virtually Yours, published by Herb Sutter and Jim Hyslop, you can find this article on the CUJ site, in addition, there have been translations in 9CBS.