📄 ch10.htm
字号:
24: {}25:26: Counter::Counter():27: itsVal(0)28: {}29:30: Counter Counter::Add(const Counter & rhs)31: {32: return Counter(itsVal+ rhs.GetItsVal());33: }34:35: int main()36: {37: Counter varOne(2), varTwo(4), varThree;38: varThree = varOne.Add(varTwo);39: cout << "varOne: " << varOne.GetItsVal()<< endl;40: cout << "varTwo: " << varTwo.GetItsVal() << endl;41: cout << "varThree: " << varThree.GetItsVal() << endl;42:43: return 0;<TT>44: }</TT>Output: varOne: 2varTwo: 4varThree: 6</FONT></PRE><P><FONT COLOR="#0066FF"><BR></FONT><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>The <TT>Add()</TT>functionis declared on line 15. It takes a constant <TT>Counter</TT> reference, which isthe number to add to the current object. It returns a <TT>Counter</TT> object, whichis the result to be assigned to the left side of the assignment statement, as shownon line 38. That is, <TT>VarOne</TT> is the object, <TT>varTwo</TT> is the parameterto the <TT>Add()</TT> function, and the result is assigned to <TT>VarThree</TT>.<BR><BR>In order to create <TT>varThree</TT> without having to initialize a value for it,a default constructor is required. The default constructor initializes <TT>itsVal</TT>to <TT>0</TT>, as shown on lines 26-28. Since <TT>varOne</TT> and <TT>varTwo</TT>need to be initialized to a non-zero value, another constructor was created, as shownon lines 22-24. Another solution to this problem is to provide the default value<TT>0</TT> to the constructor declared on line 11.<H4 ALIGN="CENTER"><A NAME="Heading49"></A><FONT COLOR="#000077">Overloading operator+</FONT></H4><P>The <TT>Add()</TT> function itself is shown on lines 30-33. It works, but itsuse is unnatural. Overloading the <TT>+</TT> operator would make for a more naturaluse of the <TT>Counter</TT> class. Listing 10.14 illustrates this.</P><P><A NAME="Heading50"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 10.14. operator+.</B></FONT></P><PRE><FONT COLOR="#0066FF">1: // Listing 10.142: //Overload operator plus (+)3:4: typedef unsigned short USHORT;5: #include <iostream.h>6:7: class Counter8: {9: public:10: Counter();11: Counter(USHORT initialValue);12: ~Counter(){}13: USHORT GetItsVal()const { return itsVal; }14: void SetItsVal(USHORT x) {itsVal = x; }15: Counter operator+ (const Counter &);16: private:17: USHORT itsVal;18: };19:20: Counter::Counter(USHORT initialValue):21: itsVal(initialValue)22: {}23:24: Counter::Counter():25: itsVal(0)26: {}27:28: Counter Counter::operator+ (const Counter & rhs)29: {30: return Counter(itsVal + rhs.GetItsVal());31: }32:33: int main()34: {35: Counter varOne(2), varTwo(4), varThree;36: varThree = varOne + varTwo;37: cout << "varOne: " << varOne.GetItsVal()<< endl;38: cout << "varTwo: " << varTwo.GetItsVal() << endl;39: cout << "varThree: " << varThree.GetItsVal() << endl;40:41: return 0;<TT>42: }</TT></FONT><FONT COLOR="#0066FF">Output: varOne: 2varTwo: 4varThree: 6</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B><TT>operator+</TT> is declaredon line 15 and defined on lines 28-31. Compare these with the declaration and definitionof the <TT>Add()</TT> function in the previous listing; they are nearly identical.The syntax of their use, however, is quite different. It is more natural to say this:</P><PRE><FONT COLOR="#0066FF">varThree = varOne + varTwo;</FONT></PRE><P>than to say:</P><PRE><FONT COLOR="#0066FF">varThree = varOne.Add(varTwo);</FONT></PRE><P>Not a big change, but enough to make the program easier to use and understand.<BLOCKQUOTE> <P><HR><FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>The techniques used for overloading <TT>operator++</TT> can be applied to the other unary operators, such as <TT>operator-</TT>. <HR></BLOCKQUOTE><H4 ALIGN="CENTER"><A NAME="Heading52"></A><FONT COLOR="#000077">Operator Overloading:Binary Operators</FONT></H4><P>Binary operators are created like unary operators, except that they do take aparameter. The parameter is a constant reference to an object of the same type. Example1</P><PRE><FONT COLOR="#0066FF">Counter Counter::operator+ (const Counter & rhs);</FONT></PRE><P>Example 2</P><PRE><FONT COLOR="#0066FF">Counter Counter::operator-(const Counter & rhs);</FONT></PRE><H4 ALIGN="CENTER"><A NAME="Heading53"></A><FONT COLOR="#000077">Issues in OperatorOverloading</FONT></H4><P>Overloaded operators can be member functions, as described in this chapter, ornon-member functions. The latter will be described on Day 14, "Special Classesand Functions," when we discuss <TT>friend</TT> functions.</P><P>The only operators that must be class members are the assignment (<TT>=</TT>),subscript(<TT>[]</TT>), function call (<TT>()</TT>), and indirection (<TT>-></TT>)operators.</P><P><TT>operator[]</TT> will be discussed tomorrow, when arrays are covered. Overloading<TT>operator-></TT> will be discussed on Day 14, when smart pointers are discussed.<H4 ALIGN="CENTER"><A NAME="Heading54"></A><FONT COLOR="#000077">Limitations on OperatorOverloading</FONT></H4><P>Operators on built-in types (such as <TT>int</TT>) cannot be overloaded. The precedenceorder cannot be changed, and the arity of the operator, that is, whether it is unaryor binary, cannot be changed. You cannot make up new operators, so you cannot declare<TT>**</TT> to be the "power of" operator.</P><DL> <DD><HR><FONT COLOR="#000077"><B>New Term:</B></FONT><B> </B><I>Arity</I> refers to how many terms are used in the operator. Some C++ operators are unary and use only one term (<TT>myValue++</TT>). Some operators are binary and use two terms (<TT>a+b</TT>). Only one operator is ternary and uses three terms. The <TT>?</TT> operator is often called the ternary operator, as it is the only ternary operator in C++ (<TT>a > b ? x : y</TT>). <HR></DL><H4 ALIGN="CENTER"><A NAME="Heading55"></A><FONT COLOR="#000077">What to Overload</FONT></H4><P>Operator overloading is one of the aspects of C++ most overused and abused bynew programmers. It is tempting to create new and interesting uses for some of themore obscure operators, but these invariably lead to code that is confusing and difficultto read.</P><P>Of course, making the <TT>+</TT> operator subtract and the <TT>*</TT> operatoradd can be fun, but no professional programmer would do that. The greater dangerlies in the well-intentioned but idiosyncratic use of an operator--using <TT>+</TT>to mean concatenate a series of letters, or <TT>/</TT> to mean split a string. Thereis good reason to consider these uses, but there is even better reason to proceedwith caution. Remember, the goal of overloading operators is to increase usabilityand understanding.<BLOCKQUOTE> <P><HR><B>DO</B> use operator overloading when it will clarify the program. <B>DON'T</B> create counter-intuitive operators. <B>DO </B>return an object of the class from overloaded operators. <HR></BLOCKQUOTE><H4 ALIGN="CENTER"><A NAME="Heading56"></A><FONT COLOR="#000077">The Assignment Operator</FONT></H4><P>The fourth and final function that is supplied by the compiler, if you don't specifyone, is the assignment operator (<TT>operator=()</TT>). This operator is called wheneveryou assign to an object. For example:</P><PRE><FONT COLOR="#0066FF">CAT catOne(5,7);CAT catTwo(3,4);// ... other code herecatTwo = catOne;</FONT></PRE><P>Here, <TT>catOne</TT> is created and initialized with <TT>itsAge</TT> equal to5 and <TT>itsWeight</TT> equal to <TT>7</TT>. <TT>catTwo</TT> is then created andassigned the values <TT>3</TT> and <TT>4</TT>.</P><P>After a while, <TT>catTwo</TT> is assigned the values in <TT>catOne</TT>. Twoissues are raised here: What happens if <TT>itsAge</TT> is a pointer, and what happensto the original values in <TT>catTwo</TT>?</P><P>Handling member variables that store their values on the free store was discussedearlier during the examination of the copy constructor. The same issues arise here,as you saw illustrated in Figures 10.1 and 10.2.</P><P>C++ programmers differentiate between a shallow or member-wise copy on the onehand, and a deep copy on the other. A shallow copy just copies the members, and bothobjects end up pointing to the same area on the free store. A deep copy allocatesthe necessary memory. This is illustrated in Figure 10.3.</P><P>There is an added wrinkle with the assignment operator, however. The object <TT>catTwo</TT>already exists and has memory already allocated. That memory must be deleted if thereis to be no memory leak. But what happens if you assign <TT>catTwo</TT> to itself?</P><PRE><FONT COLOR="#0066FF">catTwo = catTwo;</FONT></PRE><P>No one is likely to do this on purpose, but the program must be able to handleit. More important, it is possible for this to happen by accident when referencesand dereferenced pointers hide the fact that the assignment is to itself.</P><P>If you did not handle this problem carefully, <TT>catTwo</TT> would delete itsmemory allocation. Then, when it was ready to copy in the memory from the right-handside of the assignment, it would have a very big problem: The memory would be gone.</P><P>To protect against this, your assignment operator must check to see if the right-handside of the assignment operator is the object itself. It does this by examining the<TT>this</TT> pointer. Listing 10.15 shows a class with an assignment operator.</P><P><A NAME="Heading57"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 10.15. An assignmentoperator.</B></FONT></P><PRE><FONT COLOR="#0066FF">1: // Listing 10.152: // Copy constructors3:4: #include <iostream.h>5:6: class CAT7: {8: public:9: CAT(); // default constructor10: // copy constructor and destructor elided!11: int GetAge() const { return *itsAge; }12: int GetWeight() const { return *itsWeight; }13: void SetAge(int age) { *itsAge = age; }14: CAT operator=(const CAT &);15:16: private:17: int *itsAge;18: int *itsWeight;19: };20:21: CAT::CAT()22: {23: itsAge = new int;24: itsWeight = new int;25: *itsAge = 5;26: *itsWeight = 9;27: }28:29:30: CAT CAT::operator=(const CAT & rhs)31: {32: if (this == &rhs)33: return *this;34: delete itsAge;35: delete itsWeight;36: itsAge = new int;37: itsWeight = new int;38: *itsAge = rhs.GetAge();39: *itsWeight = rhs.GetWeight();40: return *this;41: }42:43:44: int main()45: {46: CAT frisky;47: cout << "frisky's age: " << frisky.GetAge() << endl;48: cout << "Setting frisky to 6...\n";49: frisky.SetAge(6);50: CAT whiskers;51: cout << "whiskers' age: " << whiskers.GetAge() << endl;52: cout << "copying frisky to whiskers...\n";53: whiskers = frisky;54: cout << "whiskers' age: " << whiskers.GetAge() << endl;55: return 0;<TT>56: }</TT></FONT></PRE><PRE><FONT COLOR="#0066FF">frisky's age: 5Setting frisky to 6...whiskers' age: 5copying frisky to whiskers...whiskers' age: 6</FONT></PRE><P><FONT COLOR="#000077"><B>Output:</B></FONT><B> </B>Listing 10.15 brings back the<TT>CAT</TT> class, and leaves out the copy constructor and destructor to save room.On line 14, the assignment operator is declared, and on lines 30-41 it is defined.<BR><FONT COLOR="#000077"><B><BR>Analysis:</B></FONT><B> </B>On line 32, the current object (the <TT>CAT</TT> beingassigned to) is tested to see whether it is the same as the <TT>CAT</TT> being assigned.This is done by checking whether or not the address of <TT>rhs</TT> is the same asthe address stored in the <TT>this</TT> pointer.</P><P>This works fine for single inheritance, but if you are using multiple inheritance,as discussed on Day 13, "Polymorphism," this test will fail. An alternativetest is to dereference the <TT>this</TT> pointer and see if the two objects are thesame:</P><PRE><FONT COLOR="#0066FF">if (*this == rhs)</FONT></PRE><P>Of course, the equality operator (<TT>==</TT>) can be overloaded as well, allowingyou to determine for yourself what it means for your objects to be equal.<H3 ALIGN="CENTER"><A NAME="Heading59"></A><FONT COLOR="#000077">Conversion Operators</FONT></H3><P>What happens when you try to assign a variable of a built-in type, such as <TT>int</TT>or <TT>unsigned short</TT>, to an object of a user-defined class? Listing 10.16 bringsback the <TT>Counter</TT> class, and attempts to assign a variable of type <TT>USHORT</TT>to a <TT>Counter</TT> object.<DL> <DD><HR><FONT COLOR="#000077"><B>WARNING:</B></FONT><B> </B>Listing 10.16 will n
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -