⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch12.htm

📁 good book for learning c++ standard language
💻 HTM
📖 第 1 页 / 共 5 页
字号:
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()
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>, is
created. 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 not
overload it and does not provide a version that takes an <TT>int</TT>. This is solved
by 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.
	
<HR>


</BLOCKQUOTE>

<H3 ALIGN="CENTER"><A NAME="Heading30"></A><FONT COLOR="#000077">Virtual Methods</FONT></H3>
<P>This chapter has emphasized the fact that a <TT>Dog</TT> object is a <TT>Mammal</TT>
object. So far that has meant only that the <TT>Dog</TT> object has inherited the
attributes (data) and capabilities (methods) of its base class. In C++ the is-a relationship
runs deeper than that, however.</P>
<P>C++ extends its polymorphism to allow pointers to base classes to be assigned
to derived class objects. Thus, you can write</P>
<PRE><FONT COLOR="#0066FF">Mammal* pMammal = new Dog;
</FONT></PRE>
<P>This creates a new <TT>Dog</TT> object on the heap and returns a pointer to that
object, which it assigns to a pointer to <TT>Mammal</TT>. This is fine, because a
dog is a mammal.


<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>This is the essence of polymorphism.
	For example, you could create many different types of windows, including dialog boxes,
	scrollable windows, and list boxes, and give them each a virtual <TT>draw()</TT>
	method. By creating a pointer to a window and assigning dialog boxes and other derived
	types to that pointer, you can call <TT>draw()</TT> without regard to the actual
	run-time type of the object pointed to. The correct <TT>draw()</TT> function will
	be called. 
<HR>


</BLOCKQUOTE>

<P>You can then use this pointer to invoke any method on <TT>Mammal</TT>. What you
would like is for those methods that are overridden in <TT>Dog()</TT> to call the
correct function. Virtual functions let you do that. Listing 12.8 illustrates how
this works, and what happens with non-virtual methods.</P>
<P><A NAME="Heading31"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.8. Using
virtual methods.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">1:     //Listing 12.8 Using virtual methods
2:
3:     #include &lt;iostream.h&gt;
4:
5:     class Mammal
6:     {
7:     public:
8:        Mammal():itsAge(1) { cout &lt;&lt; &quot;Mammal constructor...\n&quot;; }
9:        ~Mammal() { cout &lt;&lt; &quot;Mammal destructor...\n&quot;; }
10:       void Move() const { cout &lt;&lt; &quot;Mammal move one step\n&quot;; }
11:       virtual void Speak() const { cout &lt;&lt; &quot;Mammal speak!\n&quot;; }
12:    protected:
13:       int itsAge;
14:
15:    };
16:
17:    class Dog : public Mammal
18:    {
19:    public:
20:       Dog() { cout &lt;&lt; &quot;Dog Constructor...\n&quot;; }
21:       ~Dog() { cout &lt;&lt; &quot;Dog destructor...\n&quot;; }
22:       void WagTail() { cout &lt;&lt; &quot;Wagging Tail...\n&quot;; }
23:       void Speak()const { cout &lt;&lt; &quot;Woof!\n&quot;; }
24:       void Move()const { cout &lt;&lt; &quot;Dog moves 5 steps...\n&quot;; }
25:    };
26:
27:    int main()
28:    {
29:
30:       Mammal *pDog = new Dog;
31:       pDog-&gt;Move();
32:       pDog-&gt;Speak();
33:
34:     return 0;
<TT>35: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: Mammal constructor...
Dog Constructor...
Mammal move one step
Woof!
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On line 11, <TT>Mammal</TT>
is provided a virtual method--<TT>speak()</TT>. The designer of this class thereby
signals that she expects this class eventually to be another class's base type. The
derived class will probably want to override this function.</P>
<P>On line 30, a pointer to <TT>Mammal</TT> is created (<TT>pDog</TT>), but it is
assigned the address of a new <TT>Dog</TT> object. Because a dog is a mammal, this
is a legal assignment. The pointer is then used to call the <TT>Move()</TT> function.
Because the compiler knows <TT>pDog</TT> only to be a <TT>Mammal</TT>, it looks to
the <TT>Mammal</TT> object to find the <TT>Move()</TT> method.</P>
<P>On line 32, the pointer then calls the <TT>Speak()</TT> method. Because <TT>Speak()</TT>
is virtual, the <TT>Speak()</TT> method overridden in <TT>Dog</TT> is invoked.</P>
<P>This is almost magical. As far as the calling function knew, it had a <TT>Mammal</TT>
pointer, but here a method on <TT>Dog</TT> was called. In fact, if you had an array
of pointers to <TT>Mammal</TT>, each of which pointed to a subclass of <TT>Mammal</TT>,
you could call each in turn and the correct function would be called. Listing 12.9
illustrates this idea.</P>
<P><A NAME="Heading33"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.9. Multiple
virtual functions called in turn.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">1:      //Listing 12.9 Multiple virtual functions called in turn
2:
3:      #include &lt;iostream.h&gt;
4:
5:     class Mammal
6:     {
7:     public:
8:        Mammal():itsAge(1) {  }
9:        ~Mammal() { }
10:       virtual void Speak() const { cout &lt;&lt; &quot;Mammal speak!\n&quot;; }
11:    protected:
12:       int itsAge;
13:    };
14:
15:    class Dog : public Mammal
16:    {
17:    public:
18:       void Speak()const { cout &lt;&lt; &quot;Woof!\n&quot;; }
19:    };
20:
21:
22:    class Cat : public Mammal
23:    {
24:    public:
25:       void Speak()const { cout &lt;&lt; &quot;Meow!\n&quot;; }
26:    };
27:
28:
29:    class Horse : public Mammal
30:    {
31:    public:
32:       void Speak()const { cout &lt;&lt; &quot;Winnie!\n&quot;; }
33:    };
34:
35:    class Pig : public Mammal
36:    {
37:    public:
38:       void Speak()const { cout &lt;&lt; &quot;Oink!\n&quot;; }
39:    };
40:
41:    int main()
42:    {
43:       Mammal* theArray[5];
44:       Mammal* ptr;
45:       int choice, i;
46:       for ( i = 0; i&lt;5; i++)
47:       {
48:          cout &lt;&lt; &quot;(1)dog (2)cat (3)horse (4)pig: &quot;;
49:          cin &gt;&gt; choice;
50:          switch (choice)
51:          {
52:             case 1: ptr = new Dog;
53:             break;
54:             case 2: ptr = new Cat;
55:             break;
56:             case 3: ptr = new Horse;
57:             break;
58:             case 4: ptr = new Pig;
59:             break;
60:             default: ptr = new Mammal;
61:             break;
62:          }
63:          theArray[i] = ptr;
64:       }
65:       for (i=0;i&lt;5;i++)
66:          theArray[i]-&gt;Speak();
67:     return 0;
<TT>68: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: (1)dog (2)cat (3)horse (4)pig: 1
(1)dog (2)cat (3)horse (4)pig: 2
(1)dog (2)cat (3)horse (4)pig: 3
(1)dog (2)cat (3)horse (4)pig: 4
(1)dog (2)cat (3)horse (4)pig: 5
Woof!
Meow!
Winnie!
Oink!
Mammal speak!
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>This stripped-down program,
which provides only the barest functionality to each class, illustrates virtual functions
in their purest form. Four classes are declared; <TT>Dog</TT>,<TT> Cat</TT>,<TT>
Horse</TT>, and <TT>Pig</TT> are all derived from <TT>Mammal</TT>.</P>
<P>On line 10, <TT>Mammal</TT>'s <TT>Speak()</TT> function is declared to be virtual.
On lines 18, 25, 32, and 38, the four derived classes override the implementation
of <TT>Speak()</TT>.</P>
<P>The user is prompted to pick which objects to create, and the pointers are added
to the array on lines 46-64.


<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE: </B></FONT>At compile time, it is impossible to know
	which objects will be created, and thus which <TT>Speak()</TT> methods will be invoked.
	The pointer <TT>ptr</TT> is bound to its object at runtime. This is called dynamic
	binding, or run-time binding, as opposed to static binding, or compile-time binding.
	
<HR>


</BLOCKQUOTE>

<H4 ALIGN="CENTER"><A NAME="Heading35"></A><FONT COLOR="#000077">How Virtual Functions
Work</FONT></H4>
<P>When a derived object, such as a <TT>Dog</TT> object, is created, first the constructor
for the base class is called and then the constructor for the derived class is called.
Figure 12.2 shows what the <TT>Dog</TT> object looks like after it is created. Note
that the <TT>Mammal</TT> part of the object is contiguous in memory with the <TT>Dog</TT>
part.<BR>
<BR>
<A NAME="Heading36"></A><A HREF="javascript:if(confirm('http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp02.jpg  \n\nThis file was not retrieved by Teleport Pro, because the server reports that this file cannot be found.  \n\nDo you want to open it from the server?'))window.location='http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp02.jpg'" tppabs="http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp02.jpg"><FONT COLOR="#000077">Figure
12.2.</FONT></A><FONT COLOR="#000077"> </FONT><I>The <TT>Dog</TT> object after it
is created.</I> <BR>
<BR>
When a virtual function is created in an object, the object must keep track of that
function. Many compilers build a virtual function table, called a v-table. One of
these is kept for each type, and each object of that type keeps a virtual table pointer
(called a <TT>vptr</TT> or v-pointer), which points to that table.</P>
<P>While implementations vary, all compilers must accomplish the same thing, so you
won't be too wrong with this description.<BR>
<BR>
<A NAME="Heading37"></A><A HREF="javascript:if(confirm('http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp03.jpg  \n\nThis file was not retrieved by Teleport Pro, because the server reports that this file cannot be found.  \n\nDo you want to open it from the server?'))window.location='http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp03.jpg'" tppabs="http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp03.jpg"><FONT COLOR="#000077">Figure
12.3.</FONT></A><FONT COLOR="#000077"> </FONT><I>The v-table of a <TT>Mammal</TT>.</I><BR>
<BR>
Each object's <TT>vptr</TT> points to the v-table which, in turn, has a pointer to
each of the virtual functions. (Note, pointers to functions will be discussed in
depth on Day 14, &quot;Special Classes and Functions.&quot;) When the <TT>Mammal</TT>
part of the <TT>Dog</TT> is created, the <TT>vptr</TT> is initialized to point to
the correct part of the v-table, as shown in Figure 12.3.<BR>
<BR>
<A NAME="Heading38"></A><A HREF="javascript:if(confirm('http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp04.jpg  \n\nThis file was not retrieved by Teleport Pro, because the server reports that this file cannot be found.  \n\nDo you want to open it from the server?'))window.location='http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp04.jpg'" tppabs="http://petunia.atomki.hu/pio/Manuals/english/0-672/0-672-31070-8/art/ch12/12zcp04.jpg"><FONT COLOR="#000077">Figure
12.4.</FONT></A><FONT COLOR="#000077"> </FONT>The v-table of a <TT>Dog</TT>.<BR>
<BR>
When the <TT>Dog</TT> constructor is called, and the <TT>Dog</TT> part of this object
is added, the <TT>vptr</TT> is adjusted to point to the virtual function overrides
(if any) in the <TT>Dog</TT> object (see Figure 12.4) .</P>
<P>When a pointer to a <TT>Mammal</TT> is used, the <TT>vptr</TT> continues to point
to the correct function, depending on the &quot;real&quot; type of the object. Thus,
when <TT>Speak()</TT> is invoked, the correct function is invoked.
<H4 ALIGN="CENTER"><A NAME="Heading39"></A><FONT COLOR="#000077">You Cant Get There
from Here</FONT></H4>
<P>If the <TT>Dog</TT> object had a method, <TT>WagTail()</TT>, which is not in the
<TT>Mammal</TT>, you could not use the pointer to <TT>Mammal</TT> to access that
method (unless you cast it to be a pointer to <TT>Dog</TT>). Because <TT>WagTail()</TT>
is not a virtual function, and because it is not in a <TT>Mammal</TT> object, you
can't get there without either a <TT>Dog</TT> object or a <TT>Dog</TT> pointer.</P>
<P>Although you can transform the <TT>Mammal</TT> pointer into a <TT>Dog</TT> pointer,
there are usually far better and safer ways to call the <TT>WagTail()</TT> method.
C++ frowns on explicit casts because they are error-prone. This subject will be addressed
in depth when multiple inheritance is covered tomorrow, and again when templates

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -