📄 ch12.htm
字号:
11: Mammal sound!12: Tail wagging...13: Yorkie is 3 years old.14: Dobbie weighs 20 pounds.15: Dog destructor. . . 16: Mammal destructor...17: Dog destructor...18: Mammal destructor...19: Dog destructor...20: Mammal destructor...21: Dog destructor...22: Mammal destructor...23: Dog destructor...24: Mammal destructor...</FONT></PRE><P><FONT COLOR="#0000AA"><B><BR>Analysis:</B></FONT><B> </B>In Listing 12.4, <TT>Mammal</TT>'s constructor has beenoverloaded on line 11 to take an integer, the <TT>Mammal</TT>'s age. The implementationon lines 61-66 initializes <TT>itsAge</TT> with the value passed into the constructorand initializes <TT>itsWeight</TT> with the value <TT>5</TT>.</P><P><TT>Dog</TT> has overloaded five constructors, on lines 35-39. The first is thedefault constructor. The second takes the age, which is the same parameter that the<TT>Mammal</TT> constructor takes. The third constructor takes both the age and theweight, the fourth takes the age and breed, and the fifth takes the age, weight,and breed.</P><P>Note that on line 74 <TT>Dog</TT>'s default constructor calls <TT>Mammal</TT>'sdefault constructor. Although it is not strictly necessary to do this, it servesas documentation that you intended to call the base constructor, which takes no parameters.The base constructor would be called in any case, but actually doing so makes yourintentions explicit.</P><P>The implementation for the <TT>Dog</TT> constructor, which takes an integer, ison lines 80-85. In its initialization phase (lines 81-82), <TT>Dog</TT> initializesits base class, passing in the parameter, and then it initializes its breed.</P><P>Another <TT>Dog</TT> constructor is on lines 87-93. This one takes two parameters.Once again it initializes its base class by calling the appropriate constructor,but this time it also assigns <TT>weight</TT> to its base class's variable <TT>itsWeight</TT>.Note that you cannot assign to the base class variable in the initialization phase.Because <TT>Mammal</TT> does not have a constructor that takes this parameter, youmust do this within the body of the <TT>Dog</TT>'s constructor.</P><P>Walk through the remaining constructors to make sure you are comfortable withhow they work. Note what is initialized and what must wait for the body of the constructor.</P><P>The output has been numbered so that each line can be referred to in this analysis.The first two lines of output represent the instantiation of <TT>Fido</TT>, usingthe default constructor.</P><P>In the output, lines 3 and 4 represent the creation of <TT>rover</TT>. Lines 5and 6 represent <TT>buster</TT>. Note that the <TT>Mammal</TT> constructor that wascalled is the constructor that takes one integer, but the <TT>Dog</TT> constructoris the constructor that takes two integers.</P><P>After all the objects are created, they are used and then go out of scope. Aseach object is destroyed, first the <TT>Dog</TT> destructor and then the <TT>Mammal</TT>destructor is called, five of each in total.<H3 ALIGN="CENTER"><A NAME="Heading19"></A><FONT COLOR="#000077">Overriding Functions</FONT></H3><P>A <TT>Dog</TT> object has access to all the member functions in class <TT>Mammal</TT>,as well as to any member functions, such as <TT>WagTail()</TT>, that the declarationof the <TT>Dog</TT> class might add. It can also override a base class function.Overriding a function means changing the implementation of a base class functionin a derived class. When you make an object of the derived class, the correct functionis called.</P><DL> <DD><HR><FONT COLOR="#000077"><B>New Term:</B></FONT><B> </B>When a derived class creates a function with the same return type and signature as a member function in the base class, but with a new implementation, it is said to be <I>overriding </I>that method. <HR></DL><P>When you override a function, it must agree in return type and in signature withthe function in the base class. The signature is the function prototype other thanthe return type: that is, the name, the parameter list, and the keyword <TT>const</TT>if used.</P><DL> <DD><HR><FONT COLOR="#000077"><B>New Term:</B></FONT><B> </B>The <I>signature</I> of a function is its name, as well as the number and type of its parameters. The signature does not include the return type. <HR></DL><P>Listing 12.5 illustrates what happens if the <TT>Dog</TT> class overrides the<TT>Speak()</TT> method in <TT>Mammal</TT>. To save room, the accessor functionshave been left out of these classes.</P><P><A NAME="Heading20"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.5. Overridinga base class methodin a derived class.</B></FONT></P><PRE><FONT COLOR="#0066FF">1: //Listing 12.5 Overriding a base class method in a derived class2:3: #include <iostream.h>4: enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };5:6: class Mammal7: {8: public:9: // constructors10: Mammal() { cout << "Mammal constructor...\n"; }11: ~Mammal() { cout << "Mammal destructor...\n"; }12:13: //Other methods14: void Speak()const { cout << "Mammal sound!\n"; }15: void Sleep()const { cout << "shhh. I'm sleeping.\n"; }16:17:18: protected:19: int itsAge;20: int itsWeight;21: };22:23: class Dog : public Mammal24: {25: public:26:27: // Constructors28: Dog(){ cout << "Dog constructor...\n"; }29: ~Dog(){ cout << "Dog destructor...\n"; }30:31: // Other methods32: void WagTail() { cout << "Tail wagging...\n"; }33: void BegForFood() { cout << "Begging for food...\n"; }34: void Speak()const { cout << "Woof!\n"; }35:36: private:37: BREED itsBreed;38: };39:40: int main()41: {42: Mammal bigAnimal;43: Dog fido;44: bigAnimal.Speak();45: fido.Speak();46: return 0;<TT>47: }</TT></FONT><FONT COLOR="#0066FF">Output: Mammal constructor...Mammal constructor...Dog constructor...Mammal sound!Woof!Dog destructor...Mammal destructor...Mammal destructor...</FONT></PRE><P><FONT COLOR="#000077"><B><BR>Analysis:</B></FONT><B> </B>On line 34, the <TT>Dog</TT> class overrides the <TT>Speak()</TT>method, causing <TT>Dog</TT> objects to say <TT>Woof!</TT> when the <TT>Speak()</TT>method is called. On line 42, a <TT>Mammal</TT> object, <TT>bigAnimal</TT>, is created,causing the first line of output when the <TT>Mammal</TT> constructor is called.On line 43, a <TT>Dog</TT> object, <TT>fido</TT>, is created, causing the next twolines of output, where the <TT>Mammal</TT> constructor and then the <TT>Dog</TT>constructor are called.</P><P>On line 44, the <TT>Mammal</TT> object calls its <TT>Speak()</TT> method, thenon line 45, the <TT>Dog</TT> object calls its <TT>Speak()</TT> method. The outputreflects that the correct methods were called. Finally, the two objects go out ofscope and the destructors are called.<H3 ALIGN="CENTER"><A NAME="Heading22"></A><FONT COLOR="#000077">Overloading VersusOverriding</FONT></H3><P>These terms are similar, and they do similar things. When you overload a method,you create more than one method with the same name, but with a different signature.When you override a method, you create a method in a derived class with the samename as a method in the base class and the same signature.<H4 ALIGN="CENTER"><A NAME="Heading23"></A><FONT COLOR="#000077">Hiding the BaseClass Method</FONT></H4><P>In the previous listing, the <TT>Dog</TT> class's <TT>Speak()</TT> method hidesthe base class's method. This is just what is wanted, but it can have unexpectedresults. If <TT>Mammal</TT> has a method, <TT>Move()</TT>, which is overloaded, and<TT>Dog</TT> overrides that method, the <TT>Dog</TT> method will hide all of the<TT>Mammal</TT> methods with that name.</P><P>If <TT>Mammal</TT> overloads <TT>Move()</TT> as three methods--one that takesno parameters, one that takes an integer, and one that takes an integer and a direction--and<TT>Dog</TT> overrides just the <TT>Move()</TT> method that takes no parameters,it will not be easy to access the other two methods using a <TT>Dog</TT> object.Listing 12.6 illustrates this problem.</P><P><A NAME="Heading24"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.6. Hidingmethods.</B></FONT></P><PRE><FONT COLOR="#0066FF">1: //Listing 12.6 Hiding methods2:3: #include <iostream.h>4:5: class Mammal6: {7: public:8: void Move() const { cout << "Mammal move one step\n"; }9: void Move(int distance) const 10: { 11: cout << "Mammal move ";12: cout << distance <<" _steps.\n"; 13: }14: protected:15: int itsAge;16: int itsWeight;17: };18:19: class Dog : public Mammal20: {21: public:22: // You may receive a warning that you are hiding a function!23: void Move() const { cout << "Dog move 5 steps.\n"; }24: }; 25:26: int main()27: {28: Mammal bigAnimal;29: Dog fido;30: bigAnimal.Move();31: bigAnimal.Move(2);32: fido.Move();33: // fido.Move(10);34: return 0;<TT>35: }</TT></FONT><FONT COLOR="#0066FF">Output: Mammal move one stepMammal move 2 steps.Dog move 5 steps.</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>All of the extra methodsand data have been removed from these classes. On lines 8 and 9, the <TT>Mammal</TT>class declares the overloaded <TT>Move()</TT> methods. On line 18, <TT>Dog</TT> overridesthe version of <TT>Move()</TT> with no parameters. These are invoked on lines 30-32,and the output reflects this as executed.</P><P>Line 33, however, is commented out, as it causes a compile-time error. While the<TT>Dog</TT> class could have called the <TT>Move(int)</TT> method if it had notoverridden the version of<TT> Move()</TT> without parameters, now that it has doneso it must override both if it wishes to use both. This is reminiscent of the rulethat if you supply any constructor, the compiler will no longer supply a defaultconstructor.</P><P>It is a common mistake to hide a base class method when you intend to overrideit, by forgetting to include the keyword <TT>const</TT>. <TT>const</TT> is part ofthe signature, and leaving it off changes the signature and thus hides the methodrather than overriding it.<H3 ALIGN="CENTER"><A NAME="Heading26"></A><FONT COLOR="#000077">Overriding VersusHiding</FONT></H3><P>In the next section, virtual methods are described. Overriding a virtual methodsupports polymorphism--hiding it undermines polymorphism. You'll see more on thisvery soon.<H4 ALIGN="CENTER"><A NAME="Heading27"></A><FONT COLOR="#000077">Calling the BaseMethod</FONT></H4><P>If you have overridden the base method, it is still possible to call it by fullyqualifying the name of the method. You do this by writing the base name, followedby two colons and then the method name. For example: <TT>Mammal::Move()</TT>.</P><P>It would have been possible to rewrite line 28 in Listing 12.6 so that it wouldcompile, by writing</P><PRE><FONT COLOR="#0066FF">28: fido.Mammal::Move(10);</FONT></PRE><P>This calls the <TT>Mammal</TT> method explicitly. Listing 12.7 fully illustratesthis idea.</P><P><A NAME="Heading28"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.7. Callingbase method from overridden method.</B></FONT></P><PRE><FONT COLOR="#0066FF">1: //Listing 12.7 Calling base method from overridden method.2:3: #include <iostream.h>4:5: class Mammal6: {7: public:8: void Move() const { cout << "Mammal move one step\n"; }9: void Move(int distance) const 10: { 11: cout << "Mammal move " << distance;12: cout << " steps.\n"; 13: }14:15: protected:16: int itsAge;17: int itsWeight;18: };19:20: class Dog : public Mammal21: {22: public:23: void Move()const;24:25: };26:27: void Dog::Move() const28: {29: cout << "In dog move...\n";30: Mammal::Move(3);31: }32:33: int main()34: {35: Mammal bigAnimal;36: Dog fido;37: bigAnimal.Move(2);38: fido.Mammal::Move(6);39: return 0;<TT>40: }</TT></FONT><FONT COLOR="#0066FF">Output: Mammal move 2 steps.Mammal move 6 steps.</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On line 35, a <TT>Mammal</TT>,<TT>bigAnimal</TT>, is created, and on line 36, a <TT>Dog</TT>, <TT>fido</TT>, iscreated. The method call on line 37 invokes the <TT>Move()</TT> method of <TT>Mammal</TT>,which takes an <TT>int</TT>.</P><P>The programmer wanted to invoke <TT>Move(int)</TT> on the <TT>Dog</TT> object,but had a problem. <TT>Dog</TT> overrides the <TT>Move()</TT> method, but does notoverload it and does not provide a version that takes an <TT>int</TT>. This is solvedby the explicit call to the base class <TT>Move(int)</TT> method on line 33.<BLOCKQUOTE> <P><HR><B>DO</B> extend the functionality of tested classes by deriving. <B>DO</B> change the behavior of certain functions in the derived class by overriding the base class methods. <B>DON'T</B> hide a base class function by changing the function signature.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -