Ivor Horton’s BeginningVisual C++ 2008 phần 3 ppt

139 297 0
Ivor Horton’s BeginningVisual C++ 2008 phần 3 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

function, but this doesn’t have to be so. You can use longer more expressive parameter names in the func- tion prototype to aid understanding of the significance of the parameters and then use shorter parameter names in the function definition where the longer names would make the code in the body of the func- tion less readable. If you like, you can even omit the names altogether in the prototype, and just write: double power(double, int); This provides enough information for the compiler to do its job; however, it’s better practice to use some meaningful name in a prototype because it aids readability and, in some cases, makes all the difference between clear code and confusing code. If you have a function with two parameters of the same type (suppose our index was also of type double in the function power(), for example), the use of suitable names indicates which parameter appears first and which second. Try It Out Using a Function You can see how all this goes together in an example exercising the power() function. // Ex5_01.cpp // Declaring, defining, and using a function #include <iostream> using std::cout; using std::endl; double power(double x, int n); // Function prototype int main(void) { int index = 3; // Raise to this power double x = 3.0; // Different x from that in function power double y = 0.0; y = power(5.0, 3); // Passing constants as arguments cout << endl << “5.0 cubed = “ << y; cout << endl << “3.0 cubed = “ << power(3.0, index); // Outputting return value x = power(x, power(2.0, 2.0)); // Using a function as an argument cout << endl // with auto conversion of 2nd parameter << “x = “ << x; cout << endl; return 0; } // Function to compute positive integral powers of a double value // First argument is value, second argument is power index double power(double x, int n) 244 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 244 { // Function body starts here double result = 1.0; // Result stored here for(int i = 1; i <= n; i++) result *= x; return result; } // and ends here This program shows some of the ways in which you can use the function power(), specifying the argu- ments to the function in a variety of ways. If you run this example, you get the following output: 5.0 cubed = 125 3.0 cubed = 27 x = 81 How It Works After the usual #include statement for input/output and the using declarations, you have the proto- type for the function power(). If you were to delete this and try recompiling the program, the compiler wouldn’t be able to process the calls to the function in main() and would instead generate a whole series of error messages: error C3861: ‘power’: identifier not found and the error message: error C2365: ‘power’ : redefinition; previous definition was ‘formerly unknown identifier’ In a change from previous examples, I’ve used the new keyword void in the function main() where the parameter list would usually appear to indicate that no parameters are to be supplied. Previously, I left the parentheses enclosing the parameter list empty, which is also interpreted in C++ as indicating that there are no parameters; but it’s better to specify the fact by using the keyword void. As you saw, the keyword void can also be used as the return type for a function to indicate that no value is returned. If you specify the return type of a function as void, you must not place a value in any return statement within the function; otherwise, you get an error message from the compiler. You gathered from some of the previous examples that using a function is very simple. To use the func- tion power() to calculate 5.0 3 and store the result in a variable y in our example, you have the following statement: y = power(5.0, 3); The values 5.0 and 3 here are the arguments to the function. They happen to be constants, but you can use any expression as an argument, as long as a value of the correct type is ultimately produced. The arguments to the power() function substitute for the parameters x and n, which were used in the definition of the func- tion. The computation is performed using these values and then a copy of the result, 125, is returned to the calling function, main(), which is then stored in y. You can think of the function as having this value in the statement or expression in which it appears. You then output the value of y: cout << endl << “5.0 cubed = “ << y; 245 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 245 The next call of the function is used within the output statement: cout << endl << “3.0 cubed = “ << power(3.0, index); // Outputting return value Here, the value returned by the function is transferred directly to the output stream. Because you haven’t stored the returned value anywhere, it is otherwise unavailable to you. The first argument in the call of the function here is a constant; the second argument is a variable. The function power() is used next in this statement: x = power(x, power(2.0, 2.0)); // Using a function as an argument Here the power() function is called twice. The first call to the function is the rightmost in the expression, and the result supplies the value for the second argument to the leftmost call. Although the arguments in the sub-expression power(2.0, 2.0) are both specified as the double literal 2.0, the function is actu- ally called with the first argument as 2.0 and the second argument as the integer literal, 2. The compiler converts the double value specified for the second argument to type int because it knows from the function prototype (shown again below) that the type of the second parameter has been specified as int. double power(double x, int n); // Function prototype The double result 4.0 is returned by the first call to the power() function, and after conversion to type int, the value 4 is passed as the second argument in the next call of the function, with x as the first argu- ment. Because x has the value 3.0, the value of 3.0 4 is computed and the result, 81.0, stored in x. This sequence of events is illustrated in Figure 5-2. Figure 5-2 x = power( x , power( 2.0 , 2.0 )); power( 2.0 , 2 ) initial value 3.0 1 result stored back in x Converted to type int Converted to type int 4.0 (type double) 81.0 (type double) power( 3.0 , 4 ) 2 3 4 5 246 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 246 This statement involves two implicit conversions from type double to type int that were inserted by the compiler. There’s a possible loss of data when converting from type double to type int so the com- piler issues warning message when this occurs, even though the compiler has itself inserted this conver- sion. Generally relying on automatic conversions where there is potential for data loss is a dangerous programming practice, and it is not at all obvious from the code that this conversion is intended. It is far better to be explicit in your code by using the static_cast operator when necessary. The statement in the example is much better written as: x = power(x, static_cast<int>(power(2.0, 2))); Coding the statement like this avoids both the compiler warning messages that the original version caused. Using a static cast does not remove the possibility of losing data in the conversion of data from one type to another. Because you specified it though, it is clear that this is what you intended, recognizing that data loss might occur. Passing Arguments to a Function It’s very important to understand how arguments are passed to a function, as it affects how you write functions and how they ultimately operate. There are also a number of pitfalls to be avoided, so we’ll look at the mechanism for this quite closely. The arguments you specify when a function is called should usually correspond in type and sequence to the parameters appearing in the definition of the function. As you saw in the last example, if the type of an argument specified in a function call doesn’t correspond with the type of parameter in the function definition, (where possible) it converts to the required type, obeying the same rules as those for casting operands that were discussed in Chapter 2. If this proves not to be possible, you get an error message from the compiler; however, even if the conversion is possible and the code compiles, it could well result in the loss of data (for example from type long to type short) and should there- fore be avoided. There are two mechanisms used generally in C++ to pass arguments to functions. The first mechanism applies when you specify the parameters in the function definition as ordinary variables (not references). This is called the pass-by-value method of transferring data to a function so let’s look into that first of all. The Pass-by-value Mechanism With this mechanism, the variables or constants that you specify as arguments are not passed to a function at all. Instead, copies of the arguments are created and these copies are used as the values to be transferred. Figure 5-3 shows this in a diagram using the example of our power() function. Each time you call the function power(), the compiler arranges for copies of the arguments that you specify to be stored in a temporary location in memory. During execution of the functions, all references to the function parameters are mapped to these temporary copies of the arguments. 247 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 247 Figure 5-3 Try It Out Passing-by-value One consequence of the pass-by-value mechanism is that a function can’t directly modify the arguments passed. You can demonstrate this by deliberately trying to do so in an example: // Ex5_02.cpp // A futile attempt to modify caller arguments #include <iostream> using std::cout; using std::endl; int incr10(int num); // Function prototype int main(void) { int num = 3; cout << endl << “incr10(num) = “ << incr10(num) << endl int index = 2; double value = 10.0; double result = power(value, index); Temporary copies of the arguments are made for use in the function The original arguments are not accessible here, only the copies. double power ( double x , int n ) { } copy of value copy of index index 2 10.0 value 2 10.0 248 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 248 << “num = “ << num; cout << endl; return 0; } // Function to increment a variable by 10 int incr10(int num) // Using the same name might help { num += 10; // Increment the caller argument – hopefully return num; // Return the incremented value } Of course, this program is doomed to failure. If you run it, you get this output: incr10(num) = 13 num = 3 How It Works The output confirms that the original value of num remains untouched. The incrementing occurred on the copy of num that was generated and was eventually discarded on exiting from the function. Clearly, the pass-by-value mechanism provides you with a high degree of protection from having your caller arguments mauled by a rogue function, but it is conceivable that you might actually want to arrange to modify caller arguments. Of course, there is a way to do this. Didn’t you just know that pointers would turn out to be incredibly useful? Pointers as Arguments to a Function When you use a pointer as an argument, the pass-by-value mechanism still operates as before; however, a pointer is an address of another variable, and if you take a copy of this address, the copy still points to the same variable. This is how specifying a pointer as a parameter enables your function to get at a caller argument. Try It Out Pass-by-pointer You can change the last example to use a pointer to demonstrate the effect: // Ex5_03.cpp // A successful attempt to modify caller arguments #include <iostream> using std::cout; using std::endl; int incr10(int* num); // Function prototype int main(void) { 249 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 249 int num = 3; int* pnum = &num; // Pointer to num cout << endl << “Address passed = “ << pnum; int result = incr10(pnum); cout << endl << “incr10(pnum) = “ << result; cout << endl << “num = “ << num; cout << endl; return 0; } // Function to increment a variable by 10 int incr10(int* num) // Function with pointer argument { cout << endl << “Address received = “ << num; *num += 10; // Increment the caller argument // - confidently return *num; // Return the incremented value } The output from this example is: Address passed = 0012FF6C Address received = 0012FF6C incr10(pnum) = 13 num = 13 The address values produced by your computer may be different from those shown above, but the two values should be identical to each other. How It Works In this example, the principal alterations from the previous version relate to passing a pointer, pnum, in place of the original variable, num. The prototype for the function now has the parameter type specified as a pointer to int, and the main() function has the pointer pnum declared and initialized with the address of num. The function main(), and the function incr10(), output the address sent and the address received respectively, to verify that the same address is indeed being used in both places. Because the incr10() func- tion is writing to cout, you now call it before the output statement and store the return value in result: int result = incr10(pnum); cout << endl << “incr10(pnum) = “ << result; 250 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 250 This ensures proper sequencing of the output. The output shows that this time the variable num has been incremented and has a value that’s now identical to that returned by the function. In the rewritten version of the function incr10(), both the statement incrementing the value passed to the function and the return statement now de-reference the pointer to use the value stored. Passing Arrays to a Function You can also pass an array to a function, but in this case the array is not copied, even though a pass-by- value method of passing arguments still applies. The array name is converted to a pointer, and a copy of the pointer to the beginning of the array is passed by value to the function. This is quite advantageous because copying large arrays is very time consuming. As you may have worked out, however, elements of the array may be changed within a function and thus an array is the only type that cannot be passed by value. Try It Out Passing Arrays You can illustrate the ins and outs of this by writing a function to compute the average of a number of values passed to a function in an array. // Ex5_04.cpp // Passing an array to a function #include <iostream> using std::cout; using std::endl; double average(double array[], int count); //Function prototype int main(void) { double values[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; cout << endl << “Average = “ << average(values, (sizeof values)/(sizeof values[0])); cout << endl; return 0; } // Function to compute an average double average(double array[], int count) { double sum = 0.0; // Accumulate total in here for(int i = 0; i < count; i++) sum += array[i]; // Sum array elements return sum/count; // Return average } 251 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 251 The program produces the following output: Average = 5.5 How It Works The average()function is designed to work with an array of any length. As you can see from the proto- type, it accepts two arguments: the array and a count of the number of elements. Because you want it to work with arrays of arbitrary length, the array parameter appears without a dimension specified. The function is called in main() in this statement, cout << endl << “Average = “ << average(values, (sizeof values)/(sizeof values[0])); The function is called with the first argument as the array name, values, and the second argument as an expression that evaluates to the number of elements in the array. You’ll recall this expression, using the operator sizeof, from when you looked at arrays in Chapter 4. Within the body of the function, the computation is expressed in the way you would expect. There’s no significant difference between this and the way you would write the same computation if you implemented it directly in main(). The output confirms that everything works as we anticipated. Try It Out Using Pointer Notation When Passing Arrays You haven’t exhausted all the possibilities here. As you determined at the outset, the array name is passed as a pointer — to be precise, as a copy of a pointer — so within the function you are not obliged to work with the data as an array at all. You could modify the function in the example to work with pointer nota- tion throughout, in spite of the fact that you are using an array. // Ex5_05.cpp // Handling an array in a function as a pointer #include <iostream> using std::cout; using std::endl; double average(double* array, int count); //Function prototype int main(void) { double values[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; cout << endl << “Average = “ << average(values, (sizeof values)/(sizeof values[0])); 252 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 252 cout << endl; return 0; } // Function to compute an average double average(double* array, int count) { double sum = 0.0; // Accumulate total in here for(int i = 0; i < count; i++) sum += *array++; // Sum array elements return sum/count; // Return average } The output is exactly the same as in the previous example. How It Works As you can see, the program needed very few changes to make it work with the array as a pointer. The prototype and the function header have been changed, although neither change is absolutely necessary. If you change both back to the original version with the first parameter specified as a double array and leave the function body written in terms of a pointer, it works just as well. The most interesting aspect of this version is the body of the for loop statement: sum += *array++; // Sum array elements Here you apparently break the rule about not being able to modify an address specified as an array name because you are incrementing the address stored in array. In fact, you aren’t breaking the rule at all. Remember that the pass-by-value mechanism makes a copy of the original array address and passes that to the function, so you are just modifying the copy here — the original array address is quite unaffected. As a result, whenever you pass a one-dimensional array to a function, you are free to treat the value passed as a pointer in every sense, and change the address in any way that you want. Passing Multidimensional Arrays to a Function Passing a multidimensional array to a function is quite straightforward. The following statement declares a two dimensional array, beans: double beans[2][4]; You could then write the prototype of a hypothetical function, yield(), like this: double yield(double beans[2][4]); You may be wondering how the compiler can know that this is defining an array of the dimensions shown as an argument, and not a single array element. The answer is simple — you can’t write a single array element as a parameter in a function definition or prototype, although you can pass one as an argument when you call a function. For a parameter accepting a single element of an array as an argument, the parameter would have just a variable name. The array context doesn’t apply. 253 Chapter 5: Introducing Structure into Your Programs 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 253 [...]... j = i; return a[j]; // Index of lowest element // // // // Test for a lower value if so update j Return reference to lowest element } The output from this example is: 3 3 10 10 1.5 6.9 15 15 2.7 2.7 23 23 4.5 4.5 12 12 6.8 6.8 13. 5 13. 5 2.1 7.9 14 14 How It Works Let’s first take a look at how the function is implemented The prototype for the function lowest() uses double& as the specification of the... ❑ The mechanism for accepting a variable number of arguments in C++/ CLI is different from the native C++ mechanism ❑ Accessing command line arguments in main() in a C++/ CLI program is also different from the native C++ mechanism Let’s look at the last two differences in more detail Functions Accepting a Variable Number of Arguments The C++/ CLI language provides for a variable number of arguments by... factorial of 4 (written as 4!) is 4 *3* 2*1 = 24, and 3! is 3* 2*1 = 6, so it follows that 4! = 4 *3! , or more generally: fact(n) = n*fact(n - 1) The limiting case is when n is 1, in which case 1! = 1 Write a recursive function that calculates factorials, and test it 277 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 278 Chapter 5: Introducing Structure into Your Programs 2 3 The trigonometry functions (sin(),... the function calls itself n times, ignoring the sign of n The mechanism is illustrated in Figure 5-4, where the value 3 for the index argument is assumed 272 25905c05.qxd:WroxPro 2/21/08 8:48 AM Page 2 73 Chapter 5: Introducing Structure into Your Programs Result: x3 x*x*x power( x , 3 ) double power( double x, int n ) { return x*power( x , n - 1 ); } x*x 2 double power( double x, int n ) { return... argument // - confidently // Return the incremented value } This program produces the output: Value received = 3 incr10(num) = 13 num = 13 Value received = 6 incr10(value) = 16 value = 16 How It Works You should find the way this works quite remarkable This is essentially the same as Ex5_ 03. cpp, except that the function uses a reference as a parameter The prototype has been changed to reflect this... from a native C++ function, ensure that the object being returned has the correct scope Never return a pointer or a reference to an object that is local to a native C++ function ❑ In a C++/ CLI program there is no problem with returning a handle to memory that has been allocated dynamically because the garbage collector takes care of deleting it when it is no longer required ❑ When you pass a C++/ CLI array... double power(double x, int n); int main(void) { double x = 2.0; double result = 0.0; // Function prototype // Different x from that in function power // Calculate x raised to powers -3 to +3 inclusive for(int index = -3 ; index< =3 ; index++) cout . received = 3 incr10(num) = 13 num = 13 Value received = 6 incr10(value) = 16 value = 16 How It Works You should find the way this works quite remarkable. This is essentially the same as Ex5_ 03. cpp,. func- tion power() to calculate 5.0 3 and store the result in a variable y in our example, you have the following statement: y = power(5.0, 3) ; The values 5.0 and 3 here are the arguments to the. 2.0 , 2 ) initial value 3. 0 1 result stored back in x Converted to type int Converted to type int 4.0 (type double) 81.0 (type double) power( 3. 0 , 4 ) 2 3 4 5 246 Chapter 5: Introducing

Ngày đăng: 12/08/2014, 19:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan