Item 32: Make sure public inheritance simulates "is-"
By Scott Meyers
Translator: fatalerror99 (itepub's nirvana)
Release: http://blog.csdn.net/fatalerror99/
InSome must watch while some must sleep(W. H. Freeman and Company, 1974) in this book, William Dement tells a story about how he tries to make his students remember the most important things in his course. The book claims that he told his class that average British primary and secondary school students do not know much about the history of the Hastings war in 1066. He stressed that if a child remembers something, he or she remembers the 1066 age. For the students on his courses, Dement spoke endlessly, but there was only a small amount of important information, including sleeping pills, which caused insomnia. He hopes that his students will remember these few major events even if they forget everything else discussed in the course, and review these basic things over the entire semester.
At the end of the course, the last question of the final exam is: "Write down what you get from this course, one thing you will remember exactly in your life ." When Dement scored the test, he almost fainted. Almost everyone writes "1066 ".
Therefore, I have repeatedly complained to you that the only most important rule for object-oriented programming in C ++ is: Public inheritance (Public inheritance), which means "is-". Make this rule unforgettable.
If you write a Class D ("derived") Publicly inherited from Class B ("base"), you are telling the C ++ Compiler (and your code readers) every object of the D type is also an object of the B type, but vice versa. You mean that B depicts a more general concept than D, and D describes a more special concept than B. You can use an object of type B anywhere. An object of Type D can also be used, because every object whose type is D is also an object whose type is B. On the other hand, if you need an object of Type D, a object of type B will not work: Every D is a B, but vice versa.
C ++ insists on this explanation of Public inheritance. Consider this example:
Class person {...};
Class student: public person {...};
We know from our daily experience that every student is a person, but not everyone is a student. This is the meaning strictly determined by this inheritance system. We expect everything to be true for a person-for example, he or she has a birth day-for a student. We do not expect everything to be true for students-for example, he or she is registered in a particular school-for ordinary people. The concept of a person is more common than that of a student. A student is a special type of person.
In the C ++ field, any expected number type is person (or pointer-to-person or reference-to-person) all functions can accept a student object (or pointer-to-student or reference-to-student ):
Void eat (const person & P); // anyone can eat
Void Study (const student & S); // only students study
Person P; // P is a person
Student s; // s is a student
Eat (p); // fine, P is a person
Eat (s); // fine, S is a student,
// And a student is-a person
Study (s); // fine
Study (p); // error! P isn't a student
This is only true for public inheritance. Only student is derived from person in the public mode, and C ++ has the behavior described by me. Private inheritance means everything else (see item 39), and what protected inheritance actually means makes me confused so far.
Public inheritance and is-a are equivalent. It sounds simple, but sometimes your instincts mislead you. For example, a penguin is a bird, and it is OK if it can fly. If we try to express it in C ++, we will get:
Class bird {
Public:
Virtual void fly (); // birds can fly
...
};
Class Penguin: Public bird {// penguins are birds
...
};
Suddenly we were in trouble because the inheritance system showed that penguins could fly and we knew it was not true. What happened?
In this case, we became the victim of an less rigorous language-English. When we say that birds can fly, we do not mean that all types of birds can fly. We just mean that birds can fly in general. If we are more accurate, we should admit that there are several birds that cannot fly, and propose the following inheritance system, which is much better than the simulation of facts:
Class bird {
... // No Fly function is declared
};
Class flyingbird: Public bird {
Public:
Virtual void fly ();
...
};
Class Penguin: Public bird {
... // No Fly function is declared
};
This inheritance system is more loyal to what we actually know than the original design.
At this point, we have not fully done anything about these birds, because for some software systems, there may be no need to distinguish between flying birds and non-flying birds. The original two-class inheritance system may be fully applicable if your application has done a lot of processing for the beam and wings and does not intend to do anything about the flight. It is a simple reflection of the fact that there is no perfect design for all software. The best design depends on what the system expects, whether it is the present or the future. If your program knows nothing about flight and does not expect to know anything in the future, it may be a perfect design decision for a bird who cannot distinguish between flying and flying. In fact, it may be more desirable than to differentiate them, because there is no such distinction in the world you are trying to simulate ..
For how to deal with what I said, "All birds can fly, penguins are birds, and penguins cannot fly ...... Oh ......" There is another idea. That is, to redefine the fly function for Penguin to generate a runtime error.
Void error (const STD: string & MSG); // defined elsewhere
Class Penguin: Public bird {
Public:
Virtual void fly () {error ("attempt to make a penguin fly! ");}
...
};
It is important to acknowledge that "some of the things mentioned here may be different from what you think. This does not mean that "penguins cannot fly ". It means "penguins can fly, but it is a mistake to try to do so ".
(Click here, next)