📄 ch12.htm
字号:
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>The output has been numbered here
so that each line can be referred to in the analysis.
<HR>
</BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF">Output: 1: Mammal constructor...
2: Dog constructor...
3: Mammal(int) constructor...
4: Dog(int) constructor...
5: Mammal(int) constructor...
6: Dog(int, int) constructor...
7: Mammal(int) constructor...
8: Dog(int, BREED) constructor....
9: Mammal(int) constructor...
10: Dog(int, int, BREED) constructor...
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 been
overloaded on line 11 to take an integer, the <TT>Mammal</TT>'s age. The implementation
on lines 61-66 initializes <TT>itsAge</TT> with the value passed into the constructor
and 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 the
default 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 the
weight, 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>'s
default constructor. Although it is not strictly necessary to do this, it serves
as 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 your
intentions explicit.</P>
<P>The implementation for the <TT>Dog</TT> constructor, which takes an integer, is
on lines 80-85. In its initialization phase (lines 81-82), <TT>Dog</TT> initializes
its 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, you
must 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 with
how 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>, using
the default constructor.</P>
<P>In the output, lines 3 and 4 represent the creation of <TT>rover</TT>. Lines 5
and 6 represent <TT>buster</TT>. Note that the <TT>Mammal</TT> constructor that was
called is the constructor that takes one integer, but the <TT>Dog</TT> constructor
is the constructor that takes two integers.</P>
<P>After all the objects are created, they are used and then go out of scope. As
each 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 declaration
of 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 function
in a derived class. When you make an object of the derived class, the correct function
is 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 with
the function in the base class. The signature is the function prototype other than
the 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 functions
have been left out of these classes.</P>
<P><A NAME="Heading20"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.5. Overriding
a 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 class
2:
3: #include <iostream.h>
4: enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
5:
6: class Mammal
7: {
8: public:
9: // constructors
10: Mammal() { cout << "Mammal constructor...\n"; }
11: ~Mammal() { cout << "Mammal destructor...\n"; }
12:
13: //Other methods
14: 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 Mammal
24: {
25: public:
26:
27: // Constructors
28: Dog(){ cout << "Dog constructor...\n"; }
29: ~Dog(){ cout << "Dog destructor...\n"; }
30:
31: // Other methods
32: 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 two
lines 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, then
on line 45, the <TT>Dog</TT> object calls its <TT>Speak()</TT> method. The output
reflects that the correct methods were called. Finally, the two objects go out of
scope and the destructors are called.
<H3 ALIGN="CENTER"><A NAME="Heading22"></A><FONT COLOR="#000077">Overloading Versus
Overriding</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 same
name as a method in the base class and the same signature.
<H4 ALIGN="CENTER"><A NAME="Heading23"></A><FONT COLOR="#000077">Hiding the Base
Class Method</FONT></H4>
<P>In the previous listing, the <TT>Dog</TT> class's <TT>Speak()</TT> method hides
the base class's method. This is just what is wanted, but it can have unexpected
results. 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 takes
no 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. Hiding
methods.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">1: //Listing 12.6 Hiding methods
2:
3: #include <iostream.h>
4:
5: class Mammal
6: {
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 Mammal
20: {
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 step
Mammal move 2 steps.
Dog move 5 steps.
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>All of the extra methods
and 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> overrides
the 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 not
overridden the version of<TT> Move()</TT> without parameters, now that it has done
so it must override both if it wishes to use both. This is reminiscent of the rule
that if you supply any constructor, the compiler will no longer supply a default
constructor.</P>
<P>It is a common mistake to hide a base class method when you intend to override
it, by forgetting to include the keyword <TT>const</TT>. <TT>const</TT> is part of
the signature, and leaving it off changes the signature and thus hides the method
rather than overriding it.
<H3 ALIGN="CENTER"><A NAME="Heading26"></A><FONT COLOR="#000077">Overriding Versus
Hiding</FONT></H3>
<P>In the next section, virtual methods are described. Overriding a virtual method
supports polymorphism--hiding it undermines polymorphism. You'll see more on this
very soon.
<H4 ALIGN="CENTER"><A NAME="Heading27"></A><FONT COLOR="#000077">Calling the Base
Method</FONT></H4>
<P>If you have overridden the base method, it is still possible to call it by fully
qualifying the name of the method. You do this by writing the base name, followed
by 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 would
compile, 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 illustrates
this idea.</P>
<P><A NAME="Heading28"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.7. Calling
base 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 Mammal
6: {
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 Mammal
21: {
22: public:
23: void Move()const;
24:
25: };
26:
27: void Dog::Move() const
28: {
29: cout << "In dog move...\n";
30: Mammal::Move(3);
31: }
32:
33: int main()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -