Reading notes effective C + + Item 32 ensure public inheritance establishes "IS-A" model

Source: Internet
Author: User
Tags assert

1. What is the "is-a" relationship of public inheritance

The most important criterion in C + + object-oriented guidelines is that public inheritance means "is-a". Remember this rule.

If you implement a Class D (derived) public that inherits from Class B (base), you are telling the C + + compiler (which also tells the code reader) that each object of type D is also an object of type B, which in turn is not correct. You're telling me that B-D represents a more general concept, whereas D shows a more specific concept than B. You are arguing that any place where type B can be used can also use Type D, because each object of type D is an object of type B, and vice versa, that is, where you can use type D, you cannot use the type b:d is b,b not d.

C + + enforces this interpretation for public inheritance. Look at the following example:

1 class Person {...}; 2 class  Public person {...};

In everyday life we know that every student is a person, but not everyone is a student. This is what the inheritance system above claims. We expect anything that is true to people-for example, when a person has a date of birth-that is true for a student. We don't expect anything that really matters to students--for example, enrolling in a particular school--is true for the general public. The concept of man is more generalized than that of students, while students are a specific type of human being.

In the domain of C + +, any function that requires a person type (or a pointer to person or a reference to person) can also use the student parameter (or a pointer or reference):

1 voidEatConstperson& p);//anyone can eat2 3 voidStudyConststudent& s);//Only students study4 5Person p;//P is a person6 7  8 9Student s;//S is a StudentTen  OneEat (P);//Fine, p is a person A  -Eat (s);//fine, S is a Student, - //and a Student is-a person the  -Study (s);//Fine -  -Study (P);//error! p isn ' t a Student

This is only valid for public inheritance. C + + will behave as described above only when the student public inherits from person. The meaning of private inheritance is completely changed (Item), and protected inheritance is something that has puzzled me so far.

2. Public inheritance may mislead you--example one, penguins can't fly

Public inheritance and "is-a" are equivalent sounds simple, but sometimes your intuition will mislead you. For example, a penguin is a bird. It is a fact that birds can fly. If you try to represent it in C + +, the following code will be generated:

1 classBird {2  Public:3 Virtual voidFly ();//birds can fly4 5 ...                                   6 7 };8 classPenguin: PublicBird {//Penguins is birds9 Ten ...                                    One  A  -};

We suddenly get into trouble because the inheritance system shows that penguins can fly, and we know it's not true. What happened?

2.1 Methods of dealing with the above problems--more precise modeling, not defining fly

In this case, we are the victims of an imprecise language-English. When we say birds can fly, we do not say that all birds can fly, usually only the ability to do so. If we are more precise, we can identify some kinds of flightless birds, we can use the following inheritance system, it better simulate the reality:

1 classBird {2...//no fly function is declared3 4 };5 classFlyingbird: PublicBird {6  Public:7 Virtual voidfly ();8 ...9 };Ten classPenguin: PublicBird { One...//no fly function is declared A  -  -};

This inheritance system is more loyal to reality than the original design.

Things about these poultry are not over yet, because for some software systems there is no need to differentiate between flying and flightless birds. If your application pays more attention to the bird's beak and bird's wings and is indifferent to whether or not to fly, the first two-class inheritance system is sufficient. This reflects a simple fact: no ideal design is available for all software . the best design depends on what the system needs to do, both now and in the future . If your application has no knowledge of flying and will never have it, it may be a perfect and effective design decision to make a distinction between flying and not being able to fly. In fact, it may be preferable to be able to differentiate their designs, as the distinction you try to model will someday disappear from the world.

2.2 Method Two for dealing with the above problems-generating run-time errors

There is another school to deal with what I described above "all birds can fly, penguins are birds, penguins can't fly" problem. is to re-define the fly function for the penguin, but let it produce a run-time error:

1 voidErrorConstSTD::string& msg);//defined elsewhere2 classPenguin: PublicBird {3  Public:4 Virtual voidFly () {error ("attempt to make a penguin fly!"); }5 ...6 };7 8  

The above may be different from what you think, it is important to be able to identify them. The above code does not say, "Penguins can't fly." Instead, "Penguins can fly, but they will be a mistake if they try."

2.3 Distinguishing between the two--compile-time errors and runtime errors

How can you tell the difference? From the point at which the error was detected, "Penguins cannot fly" the ban can be enforced by the compiler, but it can only be detected at runtime if it violates the "penguin attempt to fly is a mistake".

To show that the "Penguin Can't Fly" limit, you have to make sure that no such function is defined for the Penguin object:

1 class Bird {2 ...                                     // no fly function is declared 3 4 }; 5 class  Public Bird {6 ...                                     // no fly function is declared 7 8 9 };

If you try to get the penguin to fly, the compiler will condemn your behavior:

1 Penguin p; 2 3 p.fly ();                                      // error! 4 5  

This differs greatly from the method that produces a run-time error. If you use the wrong way to run the times, the compiler will not say a word to P.fly's call. Item 18 explains that a good interface should be able to block invalid code at compile time, so you should prefer to be able to reject the design of the Penguin flight at compile time rather than the wrong design that can only be detected at runtime.

3. Public inheritance may mislead you--example two, rectangles and squares

You may be making concessions because you have a lack of knowledge about birds, but you can rely on your mastery of the preliminary geometry, right? How complex are the rectangles and squares?

Now answer this simple question: Should the square class be inherited from the rectangle class?

You would say, "Of course!" Everyone knows that a square is a rectangle, which is not true. "Not really, at least in school." But I think we're not in school anymore.

Consider the following code:

1 classRectangle {2  Public:3   Virtual voidSetHeight (intnewheight);4   Virtual voidSetWidth (intnewwidth);5 6 Virtual intHeight ()Const;//return current Values7 8 Virtual intWidth ()Const; 9 Ten ...                                             One  A };  -  -   the  - voidMakebigger (rectangle& R)//function to increase R 's area -  - {                                                   +  - intOldHeight =r.height ();  +  A   at  -R.setwidth (R.width () +Ten);//add ten to R ' s width -  -ASSERT (r.height () = = OldHeight);//assert that R ' s -  -   in  -}//height is unchanged

It is clear that the assertion will never go wrong, and Makebigger will only modify the width of R. The height will never be modified.

Now consider the following code, which, using public inheritance, allows the square to be treated as a rectangle:

1 classSquare: PublicRectangle {...};2 3 Square S;4 5 ...6 7ASSERT (s.width () = =s.height ());8 9 //This must BES is true for all squaresTen  One Makebigger (s); A  - //By inheritance, S is-a Rectangle, -  the  - //So we can increase it area -ASSERT (s.width () = = S.height ());//This must still is true - //For all squares

It is clear that the second assertion can never fail. By definition, the width and height of a square should be the same.

But now we have a problem. How can we make the following assertions consistent?

    • Before calling Makebigger, the height and width of s are the same;
    • In the Makebigger, the width of S is changed, but the height is not;
    • After Makebigger returns, the height and width of s are still the same. (Note that S is passed to Makebigger by reference, so Makebigger modifies the s itself, not the copy of s)

Welcome to the wonderful world of public inheritance, the intuition you learn in other areas, including math, may be different from what you want to use. The basic difficulty with the above example is that something that is suitable for a rectangle (width independent of height is modified) does not apply to squares (the length and width must be the same). But public inheritance claims that anything that applies to the base class object also applies to derived class objects. for rectangles and squares (as well as examples of sets and lists involved in ITEM38), this assertion no longer applies, so it is not correct to use public inheritance to model it. The compiler might let you do this, but as we've just seen, we can't ensure that the code behaves correctly. That's what every programmer has to learn: encoding and compiling doesn't mean it can work.

4. Use public inheritance to have new insights

In the years when using object-oriented design, software intuition can make you fail, don't fret. This knowledge is still valuable, and now you have added alternative inheritance to your design arsenal, and you must use new insights to broaden your intuition and guide you to the proper use of inheritance. When some people show you a few pages of functions, you will remember that penguins inherit from birds or squares from the rectangles that make you feel interesting. It may be the right way to deal with things, but not particularly like.

5. Other two types of relationships

The "is-a" relationship is not the only relationship that exists between classes. The relationship between the other two common classes is "has-a" and "is-implemented-in-terms-of". These relationships are introduced in Item38 and ITEM39. C + + Design errors are not uncommon because other important class relationships may be incorrectly modeled as "is-a", so you should be sure to understand the differences between these relationships and to know how best to shape them in C + +.

6. Summary

Public inheritance means "is-a". Everything applied to the base class must also be applied to derived classes, because each derived class object is a base class object.

Reading notes effective C + + Item 32 ensure public inheritance establishes "IS-A" model

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.