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

📄 ch12.htm

📁 Why C++ is the emerging standard in software development. The steps to develop a C++ program. How
💻 HTM
📖 第 1 页 / 共 5 页
字号:
	<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 theattributes (data) and capabilities (methods) of its base class. In C++ the is-a relationshipruns deeper than that, however.</P><P>C++ extends its polymorphism to allow pointers to base classes to be assignedto 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 thatobject, which it assigns to a pointer to <TT>Mammal</TT>. This is fine, because adog 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 youwould like is for those methods that are overridden in <TT>Dog()</TT> to call thecorrect function. Virtual functions let you do that. Listing 12.8 illustrates howthis works, and what happens with non-virtual methods.</P><P><A NAME="Heading31"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.8. Usingvirtual methods.</B></FONT></P><PRE><FONT COLOR="#0066FF">1:     //Listing 12.8 Using virtual methods2:3:     #include &lt;iostream.h&gt;4:5:     class Mammal6:     {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 Mammal18:    {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 stepWoof!</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 therebysignals that she expects this class eventually to be another class's base type. Thederived 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 isassigned the address of a new <TT>Dog</TT> object. Because a dog is a mammal, thisis 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 tothe <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 arrayof 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.9illustrates this idea.</P><P><A NAME="Heading33"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.9. Multiplevirtual functions called in turn.</B></FONT></P><PRE><FONT COLOR="#0066FF">1:      //Listing 12.9 Multiple virtual functions called in turn2:3:      #include &lt;iostream.h&gt;4:5:     class Mammal6:     {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 Mammal16:    {17:    public:18:       void Speak()const { cout &lt;&lt; &quot;Woof!\n&quot;; }19:    };20:21:22:    class Cat : public Mammal23:    {24:    public:25:       void Speak()const { cout &lt;&lt; &quot;Meow!\n&quot;; }26:    };27:28:29:    class Horse : public Mammal30:    {31:    public:32:       void Speak()const { cout &lt;&lt; &quot;Winnie!\n&quot;; }33:    };34:35:    class Pig : public Mammal36:    {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: 5Woof!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 functionsin 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 implementationof <TT>Speak()</TT>.</P><P>The user is prompted to pick which objects to create, and the pointers are addedto 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 FunctionsWork</FONT></H4><P>When a derived object, such as a <TT>Dog</TT> object, is created, first the constructorfor 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. Notethat 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="../art/ch12/12zcp02.jpg"><FONT COLOR="#000077">Figure12.2.</FONT></A><FONT COLOR="#000077"> </FONT><I>The <TT>Dog</TT> object after itis created.</I> <BR><BR>When a virtual function is created in an object, the object must keep track of thatfunction. Many compilers build a virtual function table, called a v-table. One ofthese 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 youwon't be too wrong with this description.<BR><BR><A NAME="Heading37"></A><A HREF="../art/ch12/12zcp03.jpg"><FONT COLOR="#000077">Figure12.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 toeach of the virtual functions. (Note, pointers to functions will be discussed indepth 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 tothe correct part of the v-table, as shown in Figure 12.3.<BR><BR><A NAME="Heading38"></A><A HREF="../art/ch12/12zcp04.jpg"><FONT COLOR="#000077">Figure12.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 objectis 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 pointto 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 Therefrom 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 thatmethod (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, youcan'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 addressedin depth when multiple inheritance is covered tomorrow, and again when templatesare covered on Day 20, &quot;Exceptions and Error Handling.&quot;<H4 ALIGN="CENTER"><A NAME="Heading40"></A><FONT COLOR="#000077">Slicing</FONT></H4><P>Note that the virtual function magic operates only on pointers and references.Passing an object by value will not enable the virtual functions to be invoked. Listing12.10 illustrates this problem.</P><P><A NAME="Heading41"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.10. Dataslicing when passing by value.</B></FONT></P><PRE><FONT COLOR="#0066FF">1:      //Listing 12.10 Data slicing with passing by value2:3:      #include &lt;iostream.h&gt;4:5:     enum BOOL { FALSE, TRUE };6:     class Mammal7:     {8:     public:9:        Mammal():itsAge(1) {  }10:       ~Mammal() { }11:       virtual void Speak() const { cout &lt;&lt; &quot;Mammal speak!\n&quot;; }12:    protected:13:       int itsAge;14:    };15:16:    class Dog : public Mammal17:    {18:    public:19:       void Speak()const { cout &lt;&lt; &quot;Woof!\n&quot;; }20:    };21:22:    class Cat : public Mammal23:    {24:    public:25:       void Speak()const { cout &lt;&lt; &quot;Meow!\n&quot;; }26:    };27:28     void ValueFunction (Mammal);29:    void PtrFunction   (Mammal*);30:    void RefFunction (Mammal&amp;);31:    int main()32:    {33:       Mammal* ptr=0;34:       int choice;35:       while (1)36:       {37:          BOOL fQuit = FALSE;38:          cout &lt;&lt; &quot;(1)dog (2)cat (0)Quit: &quot;;39:          cin &gt;&gt; choice;40:          switch (choice)41:          {42:             case 0: fQuit = TRUE;43:             break;44:             case 1: ptr = new Dog;45:             break;46:             case 2: ptr = new Cat;47:             break;48:             default: ptr = new Mammal;49:             break;50:          }51:           if (fQuit)52:             break;53:           PtrFunction(ptr);

⌨️ 快捷键说明

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