📄 ch10.htm
字号:
36:
37: CAT::~CAT()
38: {
39: delete itsAge;
40: itsAge = 0;
41: delete itsWeight;
42: itsWeight = 0;
43: }
44:
45: int main()
46: {
47: CAT frisky;
48: cout << "frisky's age: " << frisky.GetAge() << endl;
49: cout << "Setting frisky to 6...\n";
50: frisky.SetAge(6);
51: cout << "Creating boots from frisky\n";
52: CAT boots(frisky);
53: cout << "frisky's age: " << frisky.GetAge() << endl;
54: cout << "boots' age: " << boots.GetAge() << endl;
55: cout << "setting frisky to 7...\n";
56: frisky.SetAge(7);
57: cout << "frisky's age: " << frisky.GetAge() << endl;
58: cout << "boot's age: " << boots.GetAge() << endl;
59: return 0;
<TT>60: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: frisky's age: 5
Setting frisky to 6...
Creating boots from frisky
frisky's age: 6
boots' age: 6
setting frisky to 7...
frisky's age: 7
boots' age: 6
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On lines 6-19, the <TT>CAT</TT>
class is declared. Note that on line 9 a default constructor is declared, and on
line 10 a copy constructor is declared.<BR>
On lines 17 and 18, two member variables are declared, each as a pointer to an integer.
Typically there'd be little reason for a class to store <TT>int</TT> member variables
as pointers, but this was done to illustrate how to manage member variables on the
free store.</P>
<P>The default constructor, on lines 21-27, allocates room on the free store for
two <TT>int</TT> variables and then assigns values to them.</P>
<P>The copy constructor begins on line 29. Note that the parameter is <TT>rhs</TT>.
It is common to refer to the parameter to a copy constructor as <TT>rhs</TT>, which
stands for right-hand side. When you look at the assignments in lines 33 and 34,
you'll see that the object passed in as a parameter is on the right-hand side of
the equals sign. Here's how it works.</P>
<P>On lines 31 and 32, memory is allocated on the free store. Then, on lines 33 and
34, the value at the new memory location is assigned the values from the existing
<TT>CAT</TT>.</P>
<P>The parameter <TT>rhs</TT> is a <TT>CAT</TT> that is passed into the copy constructor
as a constant reference. The member function <TT>rhs.GetAge()</TT> returns the value
stored in the memory pointed to by <TT>rhs</TT>'s member variable <TT>itsAge</TT>.
As a <TT>CAT</TT> object, <TT>rhs</TT> has all the member variables of any other
<TT>CAT</TT>.</P>
<P>When the copy constructor is called to create a new <TT>CAT</TT>, an existing
<TT>CAT</TT> is passed in as a parameter. The new <TT>CAT</TT> can refer to its own
member variables directly; however, it must access <TT>rhs</TT>'s member variables
using the public accessor methods.</P>
<P>Figure 10.3 diagrams what is happening here. The values pointed to by the existing
<TT>CAT</TT> are copied to the memory allocated for the new <TT>CAT</TT><BR>
<BR>
<A NAME="Heading22"></A><A HREF="102cp03.jpg" tppabs="http://www.mcp.com/814147200/0-672/0-672-31070-8/art/ch10/102cp03.jpg"><FONT COLOR="#000077">Figure
10.3.</FONT></A><FONT COLOR="#000077"> </FONT><I>Deep copy illustrated.</I><BR>
<BR>
On line 47, a <TT>CAT</TT> called <TT>frisky</TT> is created. <TT>frisky</TT>'s age
is printed, and then his age is set to 6 on line 50. On line 52, a new <TT>CAT</TT>
<TT>boots</TT> is created, using the copy constructor and passing in <TT>frisky</TT>.
Had <TT>frisky</TT> been passed as a parameter to a function, this same call to the
copy constructor would have been made by the compiler.<BR>
<BR>
On lines 53 and 54, the ages of both <TT>CAT</TT>s are printed. Sure enough, <TT>boots</TT>
has <TT>frisky</TT>'s age, <TT>6</TT>, not the default age of <TT>5</TT>. On line
56, <TT>frisky</TT>'s age is set to <TT>7</TT>, and then the ages are printed again.
This time <TT>frisky</TT>'s age is <TT>7</TT>, but <TT>boots</TT>' age is still <TT>6</TT>,
demonstrating that they are stored in separate areas of memory.</P>
<P>When the <TT>CAT</TT>s fall out of scope, their destructors are automatically
invoked. The implementation of the <TT>CAT</TT> destructor is shown on lines 37-43.
<TT>delete</TT> is called on both pointers, <TT>itsAge</TT> and <TT>itsWeight</TT>,
returning the allocated memory to the free store. Also, for safety, the pointers
are reassigned to <TT>NULL</TT>.
<H4 ALIGN="CENTER"><A NAME="Heading23"></A><FONT COLOR="#000077">Operator Overloading</FONT></H4>
<P>C++ has a number of built-in types, including <TT>int</TT>, <TT>real</TT>, <TT>char</TT>,
and so forth. Each of these has a number of built-in operators, such as addition
(<TT>+</TT>) and multiplication (<TT>*</TT>). C++ enables you to add these operators
to your own classes as well.</P>
<P>In order to explore operator overloading fully, Listing 10.6 creates a new class,
<TT>Counter</TT>. A <TT>Counter</TT> object will be used in counting (surprise!)
in loops and other applications where a number must be incremented, decremented,
or otherwise tracked.</P>
<P><A NAME="Heading24"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 10.6. The Counter
class.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">
1: // Listing 10.6
2: // The Counter class
3:
4: typedef unsigned short USHORT;
5: #include <iostream.h>
6:
7: class Counter
8: {
9: public:
10: Counter();
11: ~Counter(){}
12: USHORT GetItsVal()const { return itsVal; }
13: void SetItsVal(USHORT x) {itsVal = x; }
14:
15: private:
16: USHORT itsVal;
17:
18: };
19:
20: Counter::Counter():
21: itsVal(0)
22: {};
23:
24: int main()
25: {
26: Counter i;
27: cout << "The value of i is " << i.GetItsVal() << endl;
28: return 0;
<TT>29: }</TT>
Output: The value of i is 0
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>As it stands, this is a pretty
useless class. It is defined on lines 7-18. Its only member variable is a <TT>USHORT</TT>.
The default constructor, which is declared on line 10 and whose implementation is
on line 20, initializes the one member variable, <TT>itsVal</TT>, to zero.<BR>
<BR>
Unlike an honest, red-blooded <TT>USHORT</TT>, the <TT>Counter</TT> object cannot
be incremented, decremented, added, assigned, or otherwise manipulated. In exchange
for this, it makes printing its value far more difficult!
<H4 ALIGN="CENTER"><A NAME="Heading26"></A><FONT COLOR="#000077">Writing an Increment
Function</FONT></H4>
<P>Operator overloading restores much of the functionality that has been stripped
out of this class. For example, there are two ways to add the ability to increment
a <TT>Counter</TT> object. The first is to write an increment method, as shown in
Listing 10.7.</P>
<P><A NAME="Heading27"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 10.7. Adding
an increment operator.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">
1: // Listing 10.7
2: // The Counter class
3:
4: typedef unsigned short USHORT;
5: #include <iostream.h>
6:
7: class Counter
8: {
9: public:
10: Counter();
11: ~Counter(){}
12: USHORT GetItsVal()const { return itsVal; }
13: void SetItsVal(USHORT x) {itsVal = x; }
14: void Increment() { ++itsVal; }
15:
16: private:
17: USHORT itsVal;
18:
19: };
20:
21: Counter::Counter():
22: itsVal(0)
23: {};
24:
25: int main()
26: {
27: Counter i;
28: cout << "The value of i is " << i.GetItsVal() << endl;
29: i.Increment();
30: cout << "The value of i is " << i.GetItsVal() << endl;
31: return 0;
<TT>32: }</TT></FONT></PRE>
<PRE><FONT COLOR="#0066FF">
Output: The value of i is 0
The value of i is 1
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>Listing 10.7 adds an <TT>Increment</TT>
function, defined on line 14. Although this works, it is cumbersome to use. The program
cries out for the ability to add a <TT>++</TT> operator, and of course this can be
done.
<H4 ALIGN="CENTER"><A NAME="Heading29"></A><FONT COLOR="#000077">Overloading the
Prefix Operator</FONT></H4>
<P>Prefix operators can be overloaded by declaring functions with the form:</P>
<PRE><FONT COLOR="#0066FF">returnType Operator op (parameters)
</FONT></PRE>
<P>Here, <TT>op</TT> is the operator to overload. Thus, the <TT>++</TT> operator
can be overloaded with the following syntax:</P>
<PRE><FONT COLOR="#0066FF">void operator++ ()
</FONT></PRE>
<P>Listing 10.8 demonstrates this alternative.</P>
<P><A NAME="Heading30"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 10.8. Overloading
operator++.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">1: // Listing 10.8
2: // The Counter class
3:
4: typedef unsigned short USHORT;
5: #include <iostream.h>
6:
7: class Counter
8: {
9: public:
10: Counter();
11: ~Counter(){}
12: USHORT GetItsVal()const { return itsVal; }
13: void SetItsVal(USHORT x) {itsVal = x; }
14: void Increment() { ++itsVal; }
15: void operator++ () { ++itsVal; }
16:
17: private:
18: USHORT itsVal;
19:
20: };
21:
22: Counter::Counter():
23: itsVal(0)
24: {};
25:
26: int main()
27: {
28: Counter i;
29: cout << "The value of i is " << i.GetItsVal() << endl;
30: i.Increment();
31: cout << "The value of i is " << i.GetItsVal() << endl;
32: ++i;
33: cout << "The value of i is " << i.GetItsVal() << endl;
34: return 0;
<TT>35: }</TT>
Output: The value of i is 0
The value of i is 1
The value of i is 2
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On line 15, <TT>operator++</TT>
is overloaded, and it's used on line 32. This is far closer to the syntax one would
expect with the <TT>Counter</TT> object. At this point, you might consider putting
in the extra abilities for which <TT>Counter</TT> was created in the first place,
such as detecting when the <TT>Counter</TT> overruns its maximum size.<BR>
There is a significant defect in the way the increment operator was written, however.
If you want to put the <TT>Counter</TT> on the right side of an assignment, it will
fail. For example:</P>
<PRE><FONT COLOR="#0066FF">Counter a = ++i;</FONT></PRE>
<P>This code intends to create a new <TT>Counter</TT>, <TT>a</TT>, and then assign
to it the value in <TT>i</TT> after <TT>i</TT> is incremented. The built-in copy
constructor will handle the assignment, but the current increment operator does not
return a <TT>Counter</TT> object. It returns <TT>void</TT>. You can't assign a <TT>void</TT>
object to a <TT>Counter</TT> object. (You can't make something from nothing!)
<H4 ALIGN="CENTER"><A NAME="Heading32"></A><FONT COLOR="#000077">Returning Types
in Overloaded Operator Functions</FONT></H4>
<P>Clearly, what you want is to return a <TT>Counter</TT> object so that it can be
assigned to another <TT>Counter</TT> object. Which object should be returned? One
approach would be to create a temporary object and return that. Listing 10.9 illustrates
this approach.</P>
<P><A NAME="Heading33"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 10.9. Returning
a temporary object.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">
1: // Listing 10.9
2: // operator++ returns a temporary object
3:
4: typedef unsigned short USHORT;
5: #include <iostream.h>
6:
7: class Counter
8: {
9: public:
10: Counter();
11: ~Counter(){}
12: USHORT GetItsVal()const { return itsVal; }
13: void SetItsVal(USHORT x) {itsVal = x; }
14: void Increment() { ++itsVal; }
15: Counter operator++ ();
16:
17: private:
18: USHORT itsVal;
19:
20: };
21:
22: Counter::Counter():
23: itsVal(0)
24: {};
25:
26: Counter Counter::operator++()
27: {
28: ++itsVal;
29: Counter temp;
30: temp.SetItsVal(itsVal);
31: return temp;
32: }
33:
34: int main()
35: {
36: Counter i;
37: cout << "The value of i is " << i.GetItsVal() << endl;
38: i.Increment();
39: cout << "The value of i is " << i.GetItsVal() << endl;
40: ++i;
41: cout << "The value of i is " << i.GetItsVal() << endl;
42: Counter a = ++i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -