📄 ch12.htm
字号:
18: public:
19: void Speak()const { cout << "Woof!\n"; }
20: };
21:
22: class Cat : public Mammal
23: {
24: public:
25: void Speak()const { cout << "Meow!\n"; }
26: };
27:
28 void ValueFunction (Mammal);
29: void PtrFunction (Mammal*);
30: void RefFunction (Mammal&);
31: int main()
32: {
33: Mammal* ptr=0;
34: int choice;
35: while (1)
36: {
37: BOOL fQuit = FALSE;
38: cout << "(1)dog (2)cat (0)Quit: ";
39: cin >> 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);
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: 1
Woof
Woof
Mammal Speak!
(1)dog (2)cat (0)Quit: 2
Meow!
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-down
versions 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 do
the 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 the
choice 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 as
a 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 after
the 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 to
just 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 to
a base object is expected. What happens when that pointer to a derived subject is
deleted? If the destructor is virtual, as it should be, the right thing happens--the
derived class's destructor is called. Because the derived class's destructor will
automatically invoke the base class's destructor, the entire object will be properly
destroyed.</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 times
when your program desperately needs to be able to pass in a pointer to a base object
and have a copy of the correct derived object that is created. A common solution
to this problem is to create a <TT>Clone()</TT> method in the base class and to make
that be virtual. The <TT>Clone()</TT> method creates a new object copy of the current
class, and returns that object.</P>
<P>Because each derived class overrides the <TT>Clone()</TT> method, a copy of the
derived 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. Virtual
copy constructor.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">1: //Listing 12.11 Virtual copy constructor
2:
3: #include <iostream.h>
4:
5: class Mammal
6: {
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 Mammal
24: {
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 Mammal
40: {
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: 1
2: Mammal constructor...
3: Dog constructor...
4: (1)dog (2)cat (3)Mammal: 2
5: Mammal constructor...
6: Cat constructor...
7: (1)dog (2)cat (3)Mammal: 3
8: 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 similar
to the previous two listings, except that a new virtual method has been added to
the <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, initializing
their 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 on
lines 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> and
its <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 stored
in a second array on line 81.</P>
<P>On line 1 of the output, the user is prompted and responds with <TT>1</TT>, choosing
to create a dog. The <TT>Mammal</TT> and <TT>Dog</TT> constructors are invoked. This
is 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 first
object, the <TT>Dog</TT>. The virtual <TT>Speak()</TT> method is called, and the
correct version of <TT>Speak()</TT> is invoked. The <TT>Clone()</TT> function is
then called, and as this is also virtual, <TT>Dog</TT>'s <TT>Clone()</TT> method
is invoked, causing the <TT>Mammal</TT> constructor and the <TT>Dog</TT> copy constructor
to 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 objects
has <TT>Speak()</TT> invoked.
<H4 ALIGN="CENTER"><A NAME="Heading47"></A><FONT COLOR="#000077">The Cost of Virtual
Methods</FONT></H4>
<P>Because objects with virtual methods must maintain a v-table, there is some overhead
in having virtual methods. If you have a very small class from which you do not expect
to 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 want
the destructor to be virtual, and the assumption will be that all other methods probably
will be virtual as well. Take a long hard look at any non-virtual methods, and be
certain 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 chapter
discussed public inheritance and virtual functions. Classes inherit all the public
and 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 this
time that base constructors are invoked and parameters can be passed to the base
class.</P>
<P>Functions in the base class can be overridden in the derived class. If the base
class 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 the
object pointed to.</P>
<P>Methods in the base class can be invoked by explicitly naming the function with
the prefix of the base class name and two colons. For example, if <TT>Dog</TT> inherits
from <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. Virtual
copy constructors can be effectively created by making a virtual member function
that 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 overridde
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -