(Click here, next to the previous article)
Can you tell the difference? From the perspective of the time when the error was detected. The ban on "Penguins cannot fly" can be enforced by the compiler, but it is a violation of the statute that "let penguins really fly". It can only be detected at runtime.
To express the restriction that "Penguin cannot fly", make sure that you do not define such a function for the penguin object:
Class bird {
... // No Fly function is declared
};
Class Penguin: Public bird {
... // No Fly function is declared
};
If you try to let penguin fly now, the compiler will punish you for your violation:
Penguin P;
P. Fly (); // error!
This is totally different from running errors. The compiler does not speak about the call to P. Fly. Item 18 explains that good interfaces can prevent illegal code during compilation, so you should replace the design that only detects at runtime with the design that blocks penguin flying attempts through the compiler.
Maybe you will admit that your bird learning knowledge may be insufficient, but you are very confident in your mastery of basic geometry, are you? I mean, how complicated can a rectangle and a square be?
Well, to answer this simple question: should class square be publicly inherited from class rectangle?
"Success !" You said, "Of course! Everyone knows that a square is a rectangle, but it is not necessarily the opposite ." This is completely correct, at least in school. But I don't think we are still in school.
Consider the following code:
Class rectangle {
Public:
Virtual void setheight (INT newheight );
Virtual void setwidth (INT newwidth );
Virtual int height () const; // return current values
Virtual int width () const;
...
};
Void makebigger (rectangle & R) // function to Increase R's Area
{
Int oldheight = R. Height ();
R. setwidth (R. Width () + 10); // Add 10 to R's width
Assert (R. Height () = oldheight); // assert that R's
} // Height is unchanged
It is clear that assertions should never fail. Makebigger only changes the r width, and its height remains unchanged.
Now, consider the following code to use public inheritance so that squares can be processed like rectangles:
Class square: Public rectangle {...};
Square S;
...
Assert (S. Width () = S. Height (); // This must be true for all squares
Makebigger (s); // by inheritance, S is-a rectangle,
// So we can increase its area
Assert (S. Width () = S. Height (); // This must still be true
// For All squares
The second assertion should never fail. According to the definition, the width and height of a square are equal.
However, how can we coordinate the following assertions?
- Before makebigger is called, the s height is equal to its width;
- In makebigger, the width of S is changed, but its height is not changed;
- After the result is returned from makebigger, the height of S must be equal to its width. (Note that S is passed into makebigger by reference, so makebigger can change s itself rather than copying S .)
Doodle?
Welcome to the wonderful world of public inheritance. Your instinct to develop in other areas of learning-including mathematics-may not help you as you expected. In this case, the basic difficulty is that it is applicable to a rectangle (its width can be changed independently of its height) is not applicable to a square (its width and height must be equal ). However, the public inheritance assertions apply to everything in the base class objects (base class Object)-everything! -- Also applies to derived class objects (derived class object ). In the case of Rectangles and squares (there is also an example of item 38 containing sets and lists), this assertion is invalid, so it is totally wrong to use public inheritance to simulate their relationship. The compiler allows you to do this, but as we have seen, it cannot ensure that the Code is correct. Every programmer must realize that only compiled code does not mean that it can work.
You don't have to worry about the software intuition you 've developed over the past few years as you approach object-oriented design. The knowledge is still valuable, but now you should add inheritance to your design candidate weapons library, you must also add new insights to your instincts to guide you in using inheritance correctly. When someone shows you a function with several pages of length, you may feel interesting if you inherit from Penguin from bird or square from rectangle in time. It may be a correct way to get close to the facts, but it is not exactly the same.
The is-a relationship does not exist in the unique relationship between two classes. The other two common inter-class relationships are "has-a" and "is-implemented-in-terms-". These relationships will be considered in item 38 and item 39. It is not uncommon to use one of these other important relationships to incorrectly simulate is-a, so make sure you understand the differences between these relationships, and know how to use them for the Best Simulation in C ++.
Things to remember
- Public inheritance means "is-". Everything that applies to base classes also applies to derived classes, because every derived class object is a base class object.