📄 ch09.htm
字号:
13: swap(x,y);14: cout << "Main. After swap, x: " << x << " y: " << y << "\n";15: return 0;16: }17:18: void swap (int &rx, int &ry)19: {20: int temp;21:22: cout << "Swap. Before swap, rx: " << rx << " ry: " << ry << "\n";23:24: temp = rx;25: rx = ry;26: ry = temp;27:28: cout << "Swap. After swap, rx: " << rx << " ry: " << ry << "\n";29:<TT>30: }</TT>Output: Main. Before swap, x:5 y: 10Swap. Before swap, rx:5 ry:10Swap. After swap, rx:10 ry:5Main. After swap, x:10, y:5</FONT></PRE><P><FONT COLOR="#000077"><TT><B>Anaylsis:</B></TT></FONT>Just as in the example withpointers, two variables are declared on line 10 and their values are printed on line12. On line 13, the function <TT>swap()</TT> is called, but note that <TT>x</TT>and <TT>y</TT>, not their addresses, are passed. The calling function simply passesthe variables.<BR>When <TT>swap()</TT> is called, program execution jumps to line 18, where the variablesare identified as references. Their values are printed on line 22, but note thatno special operators are required. These are aliases for the original values, andcan be used as such.</P><P>On lines 24-26, the values are swapped, and then they're printed on line 28. Programexecution jumps back to the calling function, and on line 14, the values are printedin <TT>main()</TT>. Because the parameters to <TT>swap()</TT> are declared to bereferences, the values from <TT>main()</TT> are passed by reference, and thus arechanged in <TT>main()</TT> as well.</P><P>References provide the convenience and ease of use of normal variables, with thepower and pass-by-reference capability of pointers!<H3 ALIGN="CENTER"><A NAME="Heading24"></A><FONT COLOR="#000077">Understanding FunctionHeaders and Prototypes</FONT></H3><P>Listing 9.6 shows <TT>swap()</TT> using pointers, and Listing 9.7 shows it usingreferences. Using the function that takes references is easier, and the code is easierto read, but how does the calling function know if the values are passed by referenceor by value? As a client (or user) of <TT>swap()</TT>, the programmer must ensurethat <TT>swap()</TT> will, in fact, change the parameters.</P><P>This is another use for the function prototype. By examining the parameters declaredin the prototype, which is typically in a header file along with all the other prototypes,the programmer knows that the values passed into <TT>swap()</TT> are passed by reference,and thus will be swapped properly.</P><P>If <TT>swap()</TT> had been a member function of a class, the class declaration,also available in a header file, would have supplied this information.</P><P>In C++, clients of classes and functions rely on the header file to tell all thatis needed; it acts as the interface to the class or function. The actual implementationis hidden from the client. This allows the programmer to focus on the problem athand and to use the class or function without concern for how it works.</P><P>When Colonel John Roebling designed the Brooklyn Bridge, he worried in detailabout how the concrete was poured and how the wire for the bridge was manufactured.He was intimately involved in the mechanical and chemical processes required to createhis materials. Today, however, engineers make more efficient use of their time byusing well-understood building materials, without regard to how their manufacturerproduced them.</P><P>It is the goal of C++ to allow programmers to rely on well-understood classesand functions without regard to their internal workings. These "component parts"can be assembled to produce a program, much the same way wires, pipes, clamps, andother parts are assembled to produce buildings and bridges.</P><P>In much the same way that an engineer examines the spec sheet for a pipe to determineits load-bearing capacity, volume, fitting size, and so forth, a C++ programmer readsthe interface of a function or class to determine what services it provides, whatparameters it takes, and what values it returns.<H3 ALIGN="CENTER"><A NAME="Heading25"></A><FONT COLOR="#000077">Returning MultipleValues</FONT></H3><P>As discussed, functions can only return one value. What if you need to get twovalues back from a function? One way to solve this problem is to pass two objectsinto the function, by reference. The function can then fill the objects with thecorrect values. Since passing by reference allows a function to change the originalobjects, this effectively lets the function return two pieces of information. Thisapproach bypasses the return value of the function, which can then be reserved forreporting errors.</P><P>Once again, this can be done with references or pointers. Listing 9.8 demonstratesa function that returns three values: two as pointer parameters and one as the returnvalue of the function.</P><P><A NAME="Heading26"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 9.8. Returningvalues with pointers.</B></FONT><PRE><FONT COLOR="#0066FF">1: //Listing 9.82: // Returning multiple values from a function3:4: #include <iostream.h>5:6: typedef unsigned short USHORT;7:8: short Factor(USHORT, USHORT*, USHORT*);9:10: int main()11: {12: USHORT number, squared, cubed;13: short error;14:15: cout << "Enter a number (0 - 20): ";16: cin >> number;17:18: error = Factor(number, &squared, &cubed);19:20: if (!error)21: {22: cout << "number: " << number << "\n";23: cout << "square: " << squared << "\n";24: cout << "cubed: " << cubed << "\n";25: }26: else27: cout << "Error encountered!!\n";28: return 0;29: }30:31: short Factor(USHORT n, USHORT *pSquared, USHORT *pCubed)32: {33: short Value = 0;34: if (n > 20)35: Value = 1;36: else37: {38: *pSquared = n*n;39: *pCubed = n*n*n;40: Value = 0;41: }42: return Value;<TT>43: }</TT>Output: Enter a number (0-20): 3number: 3square: 9cubed: 27</FONT></PRE><P><FONT COLOR="#000077"><TT><B>Anaylsis:</B></TT></FONT><TT><B> </B></TT>On line12, <TT>number</TT>, <TT>squared</TT>, and <TT>cubed</TT> are defined as <TT>USHORT</TT>s.<TT>number</TT> is assigned a value based on user input. This number and the addressesof <TT>squared</TT> and <TT>cubed</TT> are passed to the function <TT>Factor()</TT>.<BR><TT>Factor()</TT>examines the first parameter, which is passed by value. If it isgreater than <TT>20</TT> (the maximum value this function can handle), it sets <TT>returnValue</TT> to a simple error value. Note that the return value from <TT>Function()</TT>is reserved for either this error value or the value <TT>0</TT>, indicating all wentwell, and note that the function returns this value on line 42.</P><P>The actual values needed, the square and cube of <TT>number</TT>, are returnednot by using the return mechanism, but rather by changing the pointers that werepassed into the function.</P><P>On lines 38 and 39, the pointers are assigned their return values. On line 40,<TT>return Value</TT> is assigned a success value. On line 41, <TT>return Value</TT>is returned.</P><P>One improvement to this program might be to declare the following:</P><PRE><FONT COLOR="#0066FF">enum ERROR_VALUE { SUCCESS, FAILURE};</FONT></PRE><P>Then, rather than returning 0 or 1, the program could return <TT>SUCCESS</TT>or <TT>FAILURE</TT>.<H4 ALIGN="CENTER"><A NAME="Heading28"></A><FONT COLOR="#000077">Returning Valuesby Reference</FONT></H4><P>Although Listing 9.8 works, it can be made easier to read and maintain by usingreferences rather than pointers. Listing 9.9 shows the same program rewritten touse references and to incorporate the <TT>ERROR</TT> enumeration.</P><P><A NAME="Heading29"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 9.9.Listing9.8 rewritten using references.</B></FONT><PRE><FONT COLOR="#0066FF">1: //Listing 9.92: // Returning multiple values from a function3: // using references4:5: #include <iostream.h>6:7: typedef unsigned short USHORT;8: enum ERR_CODE { SUCCESS, ERROR };9:10: ERR_CODE Factor(USHORT, USHORT&, USHORT&);11:12: int main()13: {14: USHORT number, squared, cubed;15: ERR_CODE result;16:17: cout << "Enter a number (0 - 20): ";18: cin >> number;19:20: result = Factor(number, squared, cubed);21:22: if (result == SUCCESS)23: {24: cout << "number: " << number << "\n";25: cout << "square: " << squared << "\n";26: cout << "cubed: " << cubed << "\n";27: }28: else29: cout << "Error encountered!!\n";30: return 0;31: }32:33: ERR_CODE Factor(USHORT n, USHORT &rSquared, USHORT &rCubed)34: {35: if (n > 20)36: return ERROR; // simple error code37: else38: {39: rSquared = n*n;40: rCubed = n*n*n;41: return SUCCESS;42: }<TT>43: }</TT></FONT><FONT COLOR="#0066FF">Output: Enter a number (0 - 20): 3number: 3square: 9cubed: 27</FONT></PRE><P><FONT COLOR="#000077"><TT><B>Anaylsis:</B></TT></FONT><TT><B> </B></TT>Listing9.9 is identical to 9.8, with two exceptions. The <TT>ERR_CODE</TT> enumeration makesthe error reporting a bit more explicit on lines 36 and 41, as well as the errorhandling on line 22.<BR><BR>The larger change, however, is that <TT>Factor()</TT> is now declared to take referencesto <TT>squared</TT> and <TT>cubed</TT> rather than to pointers. This makes the manipulationof these parameters far simpler and easier to understand.<H3 ALIGN="CENTER"><A NAME="Heading31"></A><FONT COLOR="#000077">Passing by Referencefor Efficiency</FONT></H3><P>Each time you pass an object into a function by value, a copy of the object ismade. Each time you return an object from a function by value, another copy is made.</P><P>In the "Extra Credit" section at the end of Day 5, you learned thatthese objects are copied onto the stack. Doing so takes time and memory. For smallobjects, such as the built-in integer values, this is a trivial cost.</P><P>However, with larger, user-created objects, the cost is greater. The size of auser-created object on the stack is the sum of each of its member variables. These,in turn, can each be user-created objects, and passing such a massive structure bycopying it onto the stack can be very expensive in performance and memory consumption.</P><P>There is another cost as well. With the classes you create, each of these temporarycopies is created when the compiler calls a special constructor: the copy constructor.Tomorrow you will learn how copy constructors work and how you can make your own,but for now it is enough to know that the copy constructor is called each time atemporary copy of the object is put on the stack.</P><P>When the temporary object is destroyed, which happens when the function returns,the object's destructor is called. If an object is returned by the function by value,a copy of that object must be made and destroyed as well.</P><P>With large objects, these constructor and destructor calls can be expensive inspeed and use of memory. To illustrate this idea, Listing 9.9 creates a stripped-downuser-created object: <TT>SimpleCat</TT>. A real object would be larger and more expensive,but this is sufficient to show how often the copy constructor and destructor arecalled.</P><P>Listing 9.10 creates the <TT>SimpleCat</TT> object and then calls two functions.The first function receives the <TT>Cat</TT> by value and then returns it by value.The second one receives a pointer to the object, rather than the object itself, andreturns a pointer to the object.</P><P><A NAME="Heading32"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 9.10. Passingobjects by reference.</B></FONT><PRE><FONT COLOR="#0066FF">1: //Listing 9.102: // Passing pointers to objects3:4: #include <iostream.h>5:6: class SimpleCat7: {8: public:9: SimpleCat (); // constructor10: SimpleCat(SimpleCat&); // copy constructor11: ~SimpleCat(); // destructor12: };13:14: SimpleCat::SimpleCat()15: {16: cout << "Simple Cat Constructor...\n";17: }18:19: SimpleCat::SimpleCat(SimpleCat&)20: {21: cout << "Simple Cat Copy Constructor...\n";22: }23:24: SimpleCat::~SimpleCat()25: {26: cout << "Simple Cat Destructor...\n";27: }28:29: SimpleCat FunctionOne (SimpleCat theCat);30: SimpleCat* FunctionTwo (SimpleCat *theCat);31:32: int main()33: {34: cout << "Making a cat...\n";35: SimpleCat Frisky;36: cout << "Calling FunctionOne...\n";37: FunctionOne(Frisky);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -