📄 ch12.htm
字号:
54: RefFunction(*ptr);55: ValueFunction(*ptr);56: }57: return 0;58: }59:60: void ValueFunction (Mammal MammalValue)61: {62: MammalValue.Speak();63: }64:65: void PtrFunction (Mammal * pMammal)66: {67: pMammal->Speak();68: }69:70: void RefFunction (Mammal & rMammal)71: {72: rMammal.Speak();<TT>73: }</TT></FONT><FONT COLOR="#0066FF">Output: (1)dog (2)cat (0)Quit: 1WoofWoofMammal Speak!(1)dog (2)cat (0)Quit: 2Meow!Meow!Mammal Speak!(1)dog (2)cat (0)Quit: 0</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On lines 6-26, stripped-downversions of the <TT>Mammal</TT>, <TT>Dog</TT>, and <TT>Cat</TT> classes are declared.Three functions are declared--<TT>PtrFunction()</TT>, <TT>RefFunction()</TT>, and<TT>ValueFunction()</TT>. They take a pointer to a <TT>Mammal</TT>, a <TT>Mammal</TT>reference, and a <TT>Mammal</TT> object, respectively. All three functions then dothe same thing--they call the <TT>Speak()</TT> method.</P><P>The user is prompted to choose a <TT>Dog</TT> or <TT>Cat</TT>, and based on thechoice he makes, a pointer to the correct type is created on lines 44-49.</P><P>In the first line of the output, the user chooses <TT>Dog</TT>. The <TT>Dog</TT>object is created on the free store on line 44. The <TT>Dog</TT> is then passed asa pointer, as a reference, and by value to the three functions.</P><P>The pointer and references all invoke the virtual functions, and the <TT>Dog->Speak()</TT>member function is invoked. This is shown on the first two lines of output afterthe user's choice.</P><P>The dereferenced pointer, however, is passed by value. The function expects a<TT>Mammal</TT> object, and so the compiler slices down the <TT>Dog</TT> object tojust the <TT>Mammal</TT> part. At that point, the <TT>Mammal</TT> <TT>Speak()</TT>method is called, as reflected in the third line of output after the user's choice.</P><P>This experiment is then repeated for the <TT>Cat</TT> object, with similar results.<H4 ALIGN="CENTER"><A NAME="Heading43"></A><FONT COLOR="#000077">Virtual Destructors</FONT></H4><P>It is legal and common to pass a pointer to a derived object when a pointer toa base object is expected. What happens when that pointer to a derived subject isdeleted? If the destructor is virtual, as it should be, the right thing happens--thederived class's destructor is called. Because the derived class's destructor willautomatically invoke the base class's destructor, the entire object will be properlydestroyed.</P><P>The rule of thumb is this: If any of the functions in your class are virtual,the destructor should be as well.<H4 ALIGN="CENTER"><A NAME="Heading44"></A><FONT COLOR="#000077">Virtual Copy Constructors</FONT></H4><P>As previously stated, no constructor can be virtual. Nonetheless, there are timeswhen your program desperately needs to be able to pass in a pointer to a base objectand have a copy of the correct derived object that is created. A common solutionto this problem is to create a <TT>Clone()</TT> method in the base class and to makethat be virtual. The <TT>Clone()</TT> method creates a new object copy of the currentclass, and returns that object.</P><P>Because each derived class overrides the <TT>Clone()</TT> method, a copy of thederived class is created. Listing 12.11 illustrates how this is used.</P><P><A NAME="Heading45"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 12.11. Virtualcopy constructor.</B></FONT></P><PRE><FONT COLOR="#0066FF">1: //Listing 12.11 Virtual copy constructor2:3: #include <iostream.h>4:5: class Mammal6: {7: public:8: Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }9: ~Mammal() { cout << "Mammal destructor...\n"; }10: Mammal (const Mammal & rhs);11: virtual void Speak() const { cout << "Mammal speak!\n"; }12: virtual Mammal* Clone() { return new Mammal(*this); } 13: int GetAge()const { return itsAge; }14: protected:15: int itsAge;16: };17:18: Mammal::Mammal (const Mammal & rhs):itsAge(rhs.GetAge())19: {20: cout << "Mammal Copy Constructor...\n";21: }22:23: class Dog : public Mammal24: {25: public:26: Dog() { cout << "Dog constructor...\n"; }27: ~Dog() { cout << "Dog destructor...\n"; }28: Dog (const Dog & rhs);29: void Speak()const { cout << "Woof!\n"; }30: virtual Mammal* Clone() { return new Dog(*this); }31: };32:33: Dog::Dog(const Dog & rhs):34: Mammal(rhs)35: {36: cout << "Dog copy constructor...\n";37: }38:39: class Cat : public Mammal40: {41: public:42: Cat() { cout << "Cat constructor...\n"; }43: ~Cat() { cout << "Cat destructor...\n"; }44: Cat (const Cat &);45: void Speak()const { cout << "Meow!\n"; }46: virtual Mammal* Clone() { return new Cat(*this); }47: };48:49: Cat::Cat(const Cat & rhs):50: Mammal(rhs)51: {52: cout << "Cat copy constructor...\n";53: }54:55: enum ANIMALS { MAMMAL, DOG, CAT};56: const int NumAnimalTypes = 3;57: int main()58: {59: Mammal *theArray[NumAnimalTypes];60: Mammal* ptr;61: int choice, i;62: for ( i = 0; i<NumAnimalTypes; i++)63: {64: cout << "(1)dog (2)cat (3)Mammal: ";65: cin >> choice;66: switch (choice)67: {68: case DOG: ptr = new Dog;69: break;70: case CAT: ptr = new Cat;71: break;72: default: ptr = new Mammal;73: break;74: }75: theArray[i] = ptr;76: }77: Mammal *OtherArray[NumAnimalTypes];78: for (i=0;i<NumAnimalTypes;i++)79: {80: theArray[i]->Speak();81: OtherArray[i] = theArray[i]->Clone();82: }83: for (i=0;i<NumAnimalTypes;i++)84: OtherArray[i]->Speak();25: return 0;<TT>86: }</TT></FONT><FONT COLOR="#0066FF">1: (1)dog (2)cat (3)Mammal: 12: Mammal constructor...3: Dog constructor...4: (1)dog (2)cat (3)Mammal: 25: Mammal constructor...6: Cat constructor...7: (1)dog (2)cat (3)Mammal: 38: Mammal constructor...9: Woof!10: Mammal Copy Constructor...11: Dog copy constructor...12: Meow!13: Mammal Copy Constructor...14: Cat copy constructor...15: Mammal speak!16: Mammal Copy Constructor...17: Woof!18: Meow!19: Mammal speak!</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>Listing 12.11 is very similarto the previous two listings, except that a new virtual method has been added tothe <TT>Mammal</TT> class: <TT>Clone()</TT>. This method returns a pointer to a new<TT>Mammal</TT> object by calling the copy constructor, passing in itself (<TT>*this</TT>)as a <TT>const</TT> reference.</P><P><TT>Dog</TT> and <TT>Cat</TT> both override the <TT>Clone()</TT> method, initializingtheir data and passing in copies of themselves to their own copy constructors. Because<TT>Clone()</TT> is virtual, this will effectively create a virtual copy constructor,as shown on line 81.</P><P>The user is prompted to choose dogs, cats, or mammals, and these are created onlines 62-74. A pointer to each choice is stored in an array on line 75.</P><P>As the program iterates over the array, each object has its <TT>Speak()</TT> andits <TT>Clone()</TT> methods called, in turn, on lines 80 and 81. The result of the<TT>Clone()</TT> call is a pointer to a copy of the object, which is then storedin a second array on line 81.</P><P>On line 1 of the output, the user is prompted and responds with <TT>1</TT>, choosingto create a dog. The <TT>Mammal</TT> and <TT>Dog</TT> constructors are invoked. Thisis repeated for <TT>Cat</TT> and for <TT>Mammal</TT> on lines 4-8 of the constructor.</P><P>Line 9 of the constructor represents the call to <TT>Speak()</TT> on the firstobject, the <TT>Dog</TT>. The virtual <TT>Speak()</TT> method is called, and thecorrect version of <TT>Speak()</TT> is invoked. The <TT>Clone()</TT> function isthen called, and as this is also virtual, <TT>Dog</TT>'s <TT>Clone()</TT> methodis invoked, causing the <TT>Mammal</TT> constructor and the <TT>Dog</TT> copy constructorto be called.</P><P>The same is repeated for <TT>Cat</TT> on lines 12-14, and then for <TT>Mammal</TT>on lines 15 and 16. Finally, the new array is iterated, and each of the new objectshas <TT>Speak()</TT> invoked.<H4 ALIGN="CENTER"><A NAME="Heading47"></A><FONT COLOR="#000077">The Cost of VirtualMethods</FONT></H4><P>Because objects with virtual methods must maintain a v-table, there is some overheadin having virtual methods. If you have a very small class from which you do not expectto derive other classes, there may be no reason to have any virtual methods at all.</P><P>Once you declare any methods virtual, you've paid most of the price of the v-table(although each entry does add a small memory overhead). At that point, you'll wantthe destructor to be virtual, and the assumption will be that all other methods probablywill be virtual as well. Take a long hard look at any non-virtual methods, and becertain you understand why they are not virtual.<BLOCKQUOTE> <P><HR><B>DO</B> use virtual methods when you expect to derive from a class. <B>DO</B> use a virtual destructor if any methods are virtual. <B>DON'T </B>mark the constructor as virtual. <HR></BLOCKQUOTE><H3 ALIGN="CENTER"><A NAME="Heading48"></A><FONT COLOR="#000077">Summary</FONT></H3><P>Today you learned how derived classes inherit from base classes. This chapterdiscussed public inheritance and virtual functions. Classes inherit all the publicand protected data and functions from their base classes.</P><P>Protected access is public to derived classes and private to all other objects.Even derived classes cannot access private data or functions in their base classes.</P><P>Constructors can be initialized before the body of the constructor. It is at thistime that base constructors are invoked and parameters can be passed to the baseclass.</P><P>Functions in the base class can be overridden in the derived class. If the baseclass functions are virtual, and if the object is accessed by pointer or reference,the derived class's functions will be invoked, based on the run-time type of theobject pointed to.</P><P>Methods in the base class can be invoked by explicitly naming the function withthe prefix of the base class name and two colons. For example, if <TT>Dog</TT> inheritsfrom <TT>Mammal</TT>, <TT>Mammal</TT>'s <TT>walk()</TT> method can be called with<TT>Mammal::walk()</TT>.</P><P>In classes with virtual methods, the destructor should almost always be made virtual.A virtual destructor ensures that the derived part of the object will be freed when<TT>delete</TT> is called on the pointer. Constructors cannot be virtual. Virtualcopy constructors can be effectively created by making a virtual member functionthat calls the copy constructor.<H3 ALIGN="CENTER"><A NAME="Heading49"></A><FONT COLOR="#000077">Q&A</FONT></H3><DL> <DD><B>Q. Are inherited members and functions passed along to subsequent generations? If Dog derives from Mammal, and Mammal derives from Animal, does Dog inherit Animal's functions and data?<BR> </B><BR> <B>A.</B> Yes. As derivation continues, derived classes inherit the sum of all the functions and data in all their base classes.<BR> <BR> <B>Q. If, in the example above, Mammal overrides a function in Animal, which does Dog get, the original or the overridden function?<BR> </B><BR> <B>A.</B> If <TT>Dog</TT> inherits from <TT>Mammal</TT>, it gets the function in the state <TT>Mammal</TT> has it: the overridden function.<BR> <BR> <B>Q. Can a derived class make a public base function private?<BR> </B><BR> <B>A.</B> Yes, and it remains private for all subsequent derivation.<BR> <BR> <B>Q. Why not make all class functions virtual?<BR> </B><BR> <B>A.</B> There is overhead with the first virtual function in the creation of a v-table. After that, the overhead is trivial. Many C++ programmers feel that if one function is virtual, all others should be. Other programmers disagree, feeling that there should always be a reason for what you do.<BR> <BR> <B>Q. If a function (SomeFunc()) is virtual in a base class and is also overloaded, so as to take either an integer or two integers, and the derived class overrides the form taking one integer, what is called when a pointer to a derived object calls the two-integer form?<BR> </B><BR> <B>A.</B> The overriding of the one-<TT>int</TT> form hides the entire base class function, and thus you will get a compile error complaining that that function requires only one <TT>int</TT>.</DL><H3 ALIGN="CENTER"><A NAME="Heading50"></A><FONT COLOR="#000077">Workshop</FONT></H3><P>The Workshop provides quiz questions to help you solidify your understanding ofthe material that was covered, and exercises to provide you with experience in usingwhat you've learned. Try to answer the quiz and exercise questions before checkingthe answers in Appendix D, and make sure you understand the answers before continuingto the next chapter.<H4 ALIGN="CENTER"><A NAME="Heading51"></A><FONT COLOR="#000077">Quiz</FONT></H4><DL> <DD><B>1.</B> What is a v-table?<BR> <BR> <B>2.</B> What is a virtual destructor?<BR> <BR> <B>3.</B> How do you show the declaration of a virtual constructor?<BR> <BR> <B>4.</B> How can you create a virtual copy constructor?<BR> <BR> <B>5.</B> How do you invoke a base member function from a derived class in which you've overridden that function?<BR> <BR> <B>6.</B> How do you invoke a base member function from a derived class in which you have not overridden that function?<BR> <BR> <B>7.</B> If a base class declares a function to be virtual, and a derived class does not use the term virtual when overriding that class, is it still virtual when inherited by a third-generation class?<
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -