Effective C ++ 2e Item35

zhaozj2021-02-11  255

Inheritance and object-oriented design

Many people think that inheritance is all of the object-oriented programming. Whether this view is still waiting for argument, but the number of other chapters of this book is sufficient to prove that while performing an efficient C program design, there are more tools to listen to you, not just simply let a class from another. Class inheritance.

However, the hierarchy of the design and implementation of the class is fundamentally different from everything in the C language. Only in the inheritance and object-oriented design areas, you are most likely to fundamentally rethink how software system constructs. In addition, C provides a variety of very confusing object-oriented structural components, including public, protected and private base classes; virtual and non-virtual base classes; virtual and non-virtual member functions. These components are not only linked to each other, but also interact with other parts of C . Therefore, for each component's meaning, when should be used, how best to combine the non-objective part of C --- To really understand these, we must pay hard efforts.

Another reason that makes things more complex is that there are many different parts in C , and there are more or less seem to be the same thing. E.g:

· If you need to design a group with a common feature, this use inheritance makes all classes to derive in a common base class, or use templates to produce them from a common code framework?

· The implementation of class A is to use class B. Is A to have a data member with a type B, or let A private inherited in B?

· It is assumed to design a standard library that is not available in a standard library (Terms 49 list the container classes actually provided by the standard library). Is it using templates, or it is best to use ordinary (Void *) What is the type of security interface to implement the type of pointer?

In the terms of this chapter, I will guide everyone how to answer this class. Of course, I can't take into account as side for object-oriented design. Instead, I will concentrate on: Different parts in C , what is true, when you use a part, what you really did. For example, public inheritance means "it is a" (see Terms 35), if it makes it other meaning, it will bring trouble. Similarly, the meaning of virtual functions is "Interface must be inherited", the meaning of non-virtual functions is "interface and implementation to be inherited." It is not possible to distinguish between the meaning of them will bring endless pain to C programmers.

If you understand the meaning of C various components, you will find that you can change your own understanding of the object design. You will no longer stay in distress to distinguish between different parts that are provided by C languages, but think what you want to do for your software system. Once you know what you want, it will be a very easy thing to convert it into the corresponding C parts.

Do what you want to do, understand what you do! The importance of these two points is never lifted too much. The following terms will be discussed in detail on how to efficiently achieve these two points. Terms 44 summarizes the correspondence between C facing object-oriented structural components and their meaning. It is the best summary of this chapter, as well as a concise reference for future use.

Terms 35: Make public inheritance "is a" meaning

In the book "Some Must Watch While Some Must Sleep", William Dement told a story, saying how he gave students to remember the most important part of his course. "It is said,", he told his students. "In addition to remembering the Hastings campaign in addition to remembering the Hastings campaign, he doesn't remember other history." "I must also remember 1066 this day." But for students in his class, only few topics can cause their interest, for example, sleeping pills will cause insomnia. So he pleaded his student, even if he forgetting anything he taught in class, remember only a few important historical events. Moreover, he constantly instilled this basic view of the students throughout the semester. At the end of the semester, the last question of the final exam is, "Please write down what you will remember from the courses". When DEMENT was evaluated, he was shocked. Almost all students have written "1066".

So, here I also tell you extremely trembling sounds, an important rule in the C object-oriented programming is that public inheritance means "it is a". Be sure to remember this rule.

When writing a class D ("Derived") from class B ("Base") public inheritance, you are actually telling the compiler (and the person who reads this code): Each object of type D is also type B An object, but it is not true; you are saying: b means a broader concept than D, D represents a more specific concept than B; you are declaring: Any location that can use Type B, type D object It can also be used because the object of each type D is an object of a type B. Conversely, if a type D object is required, the object of type B can not be: each D "is a" B, but it is not true.

C uses the above explanation of public inheritance. Look at this example:

Class Person {...};

Class Student: Public Person {...};

From daily experience, we know that every student is a person, but not everyone is a student. This is exactly the above hierarchy. We hope that any fact that "people" is established ---- If you have birthday ---- also set up "students"; but we don't want, any fact that is established for "student" ---- A school is going to school ---- also established to "people". The concept of people is more wide than the concept of students; students are a specific type of person.

In the C world, any one of its parameters is a function (or Person's pointer or Person reference) can actually take a Student object (or a reference to the Student pointer or Student):

Void Dance (Const person & p); / / anyone can dance

Void Study (const student & s); // Only students learn

Person P; //p is a person Student S; // s is a student Dance (P); / / correct, P is a person

Dance (s); // correct, s is a student, // a student "is a" person

Study (s); // correct

Study (p); // error! P is not a student

Just public inheritance will be like this. That is, only Student is inherited in Person, the behavior of C will like that I am described. Private inheritance is completely another thing (see Terms 42), as for protecting inheritance, as if no one knows what it is. In addition, Student "is a" Person's fact that the Student array "is a" PERSON array. See the Terms M3 for discussion on this topic.

The equivalent relationship between public inheritance and "is a" sounds simple, but in practical applications, it may not always be so intuitive. Sometimes I don't miss you. For example, there is such a fact: Penguin is a bird; there is such a fact: the bird will fly. If you want to express these facts simply in C , we will do this:

Class bird {public: virtual void fly (); // bird will fly

...

}

Class Penguin: Public Bird {// Penguin is a bird

...

}

Suddenly we can't fall into confusion, because this level of relationship means that penguins will fly, and we know that this is not a fact. what happens?

This is caused by the language (Chinese) is not tight. It is said that the bird will fly, not to say that all birds will fly, usually, only those birds with flying ability will fly. If we are more accurate, we all know that there are many kinds of birds that will not fly, so we will provide the following hierarchies, which better reflects the reality:

Class Bird {... // No declaration of FLY function};

Class Flyingbird: public bird {public: Virtual void fly (); ...};

Class nonflyingbird: public bird {

... // No declaration of FLY function};

Class penguin: public nonflyingbird {

... // No declaration of FLY function};

This level is more loyal to what we know more than the initial design.

But about the discussion of the bird problem, it is still not fully ended. Because in some software systems, the penguin is a bird is completely suitable. For example, if the program is only with the mouth of the bird, the wings do not involve flying, the initial design is very suitable. This seems to be annoyed, but it reflects a simple fact: no design is ideal for any software. Good design is unparalleled with software systems now and future features (see Terms M32). If the program does not involve flying, and it will not be, it will be very reasonable to derive in BIRD in the future. In fact, it will be better than the design that fly and not flying, because this distinction is not used in your design. Increasing excess classes in the design level is a very bad design, just like the inheritance relationship between the class. For "All birds will fly, penguins are birds, penguins will not fly", and it can also be treated with another method. That is to redefine the fly function to the penguin, make it an runtime error:

Void Error (const string & msg); // Defined elsewhere

Class Penguin: Public Bird {public: Virtual Void fly () {Error ("Penguins CAN't fly!");}

...

}

Interpretation languages ​​such as SmallTalk like to use this method, but here is important to recognize that the above code may be completely different from what you think is completely different. It is not said that "Penguin will not fly", but said, "Penguin will fly, but let them fly a mistake."

How do you distinguish between two? This can distinguish from the time that detects the error. The "penguin will not fly" is made by the compiler, "Let Penguin is a mistake" can only be detected at runtime.

In order to express "Penguin Will Fly", don't define the fly function in the penguin object:

Class bird {

... // No declaration of FLY function};

Class nonflyingbird: public bird {

... // No declaration of FLY function};

Class penguin: public nonflyingbird {

... // No declaration of FLY function};

If you want to make your penguin, the compiler will condemn your violations:

Penguin P;

P.FLY (); // Error!

The behavior obtained with SmallTalk is completely different. With that way, the compiler will not say half words.

The method of processing C has a fundamental difference between the processing method of SmallTalk, so as long as it is programmed with C , it is necessary to do things with C . In addition, detecting errors in compile time ratios have certain technological advantages over the runtime, see Terms 46.

Maybe you will say that you are poor in terms of birds. But you can use your first-class geometric knowledge, right? I mean, the rectangle and square should always be more complicated?

Well, answer this simple question: Can a class Square (square) inheritable from class Rectangle? Rectangle ^ |? Square

"Of course!" You will not say that everyone knows that a square is a rectangle, but it is usually not established. "It is true that at least at least high school can be considered. But I don't think we are still high school students.

Take a look at the code below:

Class Rectangle {Public: Virtual Void setHeight (int newheight); Virtual Void SetWidth; INT NEWPIDTH

Virtual INT Height () const; // Returns the current value Virtual int width () const; // Returns the current value

...

}

Void makebigger (Rectangle & R) // increases the function of the R area {INT OLDHEIGHT = R.HEIGHT ();

R.SetWidth (r.Width () 10); // increase the width of R 10

Assert (r.Height () == OldHeight); // Asahi Roades the height of R.

Obviously, assertions will never fail. Makebigger only changed the width of R and the height has never been modified.

Now look at the code below, it uses public inherit, so that the square can be treated as a rectangle:

Class Square: Public Rectangle {...};

Square s;

...

Assert (S.Width () == s.HEight ()); // This is true for all squares

Makebigger (s); // By inheritance, S "is a" rectangle //, so it can increase its area assert (S.Width () == s.Height ()); // This is also true for all squares

Obviously, like the previous assertions, this assertion later will never fail. Because of the definition, the width and high of the square should be equal.

Then there is a problem now. How do we coordinate the following assertions?

· Call the width and high phase of MakeBigger; · MakeBigger internal, the width of S is changed, the height is not changed; • After returning from MakeBigger, the height of S is equal and width. (Note S is passed to Makebigger by reference, so MakeBigger modified the S itself, not the copy of S)

how about it?

Welcome to the wonderful world of public inheritance, here, your intuition in other research fields --- including mathematics --- may not be as good as you expect. For the case in the above example, the most fundamental problem is that the rules apply to the rectangle (the change in width and the high degree of non-relationships) are not suitable for squares (width, and high must be the same). But public inheritance claims: anything applies to the base class - any! ---- Also suitable for derived objects. In a rectangular and a square example (and a similar example of the set of terms 40), the claimed principle is not applicable, so use public inheritance to indicate that their relationship is only an error. Of course, the compiler will not block you, but as we can see, it cannot guarantee that the program can work normally. As each programmer knows, the code is compiled and does not mean that it can work properly. But don't worry too worry that you have accumulated software development intuition in the past, there is no use of martial arts. Those knowledge is still very valuable, but since you add inherited this tool in your own design, you have to expand your professional intuition with new eyes, and guide you to develop the correct object-oriented program. Soon, you will feel that let Penguin inherits or let Square inherited from Rectangle's idea. It is like a single function to show you now. You will feel the same. Perhaps it is the correct way to solve the problem, just not suitable.

Of course, the relationship "is a" does not exist the only relationship between the class. The other two relationships between classes are "there is a" and "implementation with ...". These relationships are discussed in terms 40 and 42. One of these two relationships is incorrectly expressed as "it is a", which will lead to erroneous design. So, be sure to understand the differences of these relationships, and how it is best to use C to represent them.

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

New Post(0)