9.2 PASSING ARGUMENTS IN C


9.2 PASSING ARGUMENTS IN C++

When a function is called, each of its parameters is initialized with the corresponding argument in the function call, provided that their types agree after taking into account any allowable type conversions.[1]

There are three different ways to pass arguments to functions in C++:[2]

  • pass by value

  • pass by pointer

  • pass by reference

To fully understand them—especially so from the standpoint of understanding the distinction between "pass by value" in C++ and "pass by value" in Java—it is best to consider the primitive types and the class types of arguments separately.

9.2.1 Passing a Primitive Type Argument by Value

The following example shows the case when an argument is passed by value in the function invocation in line (A). The called function, defined in line (B), makes a local copy of the argument object. Any changes to the local copy are not visible in the calling function, in this case main.

 
//PassPrimByValue.cc #include <iostream> using namespace std; void g(int); int main() { int x = 100; g(x); //(A) cout << x << endl; // 100 return 0; } void g(int y) { y++;} //(B)

Therefore, the function g(x) cannot cause any change in the value of x in main. As a result, the value of x printed out by cout in main will remain 100.

9.2.2 Passing a Primitive Type Argument by Pointer

Passing arguments by pointer could be thought as merely a special case of passing arguments by value, since, after all, you are passing a copy of the memory address to the called function. However, through side effect, a called function can be made to bring about changes that are visible in the calling function.

Consider the following example that shows two called functions, g( int* ) and h( int* ), one whose local changes are visible in main and one whose local changes are not.

 
//PassPrimByPointer.cc #include <iostream> using namespace std; void g(int*); void h(int*); int main() { int x = 100; int* p = &x; //(A) g(p); //(B) cout << x << endl; // 100 //(C) h(p); //(D) cout << x << endl; // 200 (changed by side effect) //(E) return 0; } void g(int* q) { //(F) int y = 200; q = &y; //(G) } void h(int* q) {*q = 200; } //(H)

When function g is called in line (B), the local variable q in line (F) is initialized to the same memory address that p is pointing to in line (A) of main. Inside g, we then change the value of q by making it point to another object in line (G). But that in no way changes what p is pointing to in main. It also does not change the contents of the memory location to which p is pointing. Therefore, the first cout in main in line (C) prints out the same value for x that was set earlier in the program.

On the other hand, the called function h in line (H) actually alters the contents of the memory location to which p is pointing in main. While the local variable q in line (H) is pointing to the same memory location as it was for the case of g, we are now actually altering the contents of that memory location. As a result, the second cout in main in line (E) will print out the new value for x—200—as set inside the function h. This is a classic example of a called function affecting change in the calling function through side effect.

9.2.3 Passing a Primitive Type Argument by Reference

In the following example, when the function g( int& ) is called in line (A) of main, the local variable y in line (C) becomes a reference for the variable x in main. In other words, the object of the reference y in line (C) is the object x inside main. As the reader should know from our Chapter 8 discussion on the reference types in C++, this causes y to serve as merely another name—an alias—for x. So any changes made to y will be reflected in x.

 
//PassPrimByRef.cc #include <iostream> using namespace std; void g(int&); int main() { int x = 100; g(x); //(A) cout << x << endl; // 101 //(B) return 0; } void g(int& y) { y++; } //(C)

Therefore, when g(x) is called in main, it brings about a change in the value of x itself through side effect. As a result, the cout in line (B) in main will print out 101.[3]

9.2.4 Passing a Class Type Argument by Value

Passing a class type argument by value works the same way as passing a primitive type argument. The called function makes a local copy of the argument elsewhere in the memory and any changes made to this local copy by the called function are not visible in the calling function.

In the following example, main first constructs a User object in line (A). The function call in line (B) passes the User object by value to the function in line (C). Because the argument is passed by value, what the local variable v in line (C) gets is a copy of the User object u of main.

 
//PassClassTypeByValue.cc #include <iostream> #include <string> using namespace std; class User { public: string name; int age; User(string nam, int yy) { name = nam; age = yy; } }; void g(User); int main() { User u("Xenon", 89); //(A) g(u); //(B) cout << u.name << " " << u.age << endl; // Xenon 89 return 0; } void g(User v) { //(C) v.name = "Yukon"; v.age = 200; }

Therefore, the changes made inside the function g are confined to the copy of the User object it receives. The original object in main remains untouched. As a result, the cout statement in main prints out the name and the age fields as set when the object was first constructed.

9.2.5 Passing a Class Type Argument by Pointer

When an argument object is passed by pointer in a C++ program, any changes made to the object inside the called function can become visible in the calling function through side effect, as was the case with the primitive type in Section 9.2.2.

In the following example, we have two functions, g(User*)and h(User*); the former's changes are not visible inside main, whereas the latter's changes are.

 
//PassClassTypeByPointer.cc #include <iostream> #include <string> using namespace std; class User { public: string name; int age; User(string nam, int yy) { name = nam; age = yy; } }; void g(User*); void h(User*); int main() { User* p = new User("Xeno", 89); //(A) g(p); //(B) cout << p->name << " " << p->age << endl; // Xeno 89 h(p); //(C) cout << p->name << " " << p->age << endl; // Yuki 200 return 0; } void g(User* q) { //(D) q = new User("Yuki", 200); //(E) } void h(User* q) { //(F) q->name = "Yuki"; //(G) q->age = 200; //(H) }

The main first constructs a User object in line (A). When g is called in line (B), the address of the User object is passed to this function in line (D). But, inside g, the value of the local pointer q, set originally to point to the User object constructed in main, is redirected to point to a new User object in line (E). However, this redirection of q has no effect on where p is pointing in main. It also has no effect on the contents of the memory where p is pointing. Therefore, g cannot cause any change inside main. So the first cout statement in main prints out the originally set values of the data members of the User object.

By contrast, the function h(User*) called in line (C) of main changes through side effect the User object created previously in main. The local pointer q inside h in line (F) will point to the User object created in main. By altering in lines (G) and (H) the contents of the memory where q points, we change the object created in main. Therefore, the second cout statement in main prints out the new state of the object, as illustrated by the commented out portion of the line.

9.2.6 Passing a Class Type Argument by Reference

In the following example, the User object constructed in line (A) in main becomes the argument in the function call in line (B). The argument is passed by reference to the function in line (C). The local variable v in line (C) serves as a reference for the User objectu in main. This makes the local variable v essentially an alias for the variable u in main. Therefore, the changes made to v in lines (D) and (E) are reflected in the state of the object u inside main.

 
//PassClassTypeByRef.cc #include <iostream> #include <string> using namespace std; class User { public: string name; int age; User(string nam, int yy) { name = nam; age = yy; } }; void g(User&); int main() { User u("Xenon", 89); //(A) g(u); //(B) cout << u.name << " " << u.age << endl; // Yukon 200 return 0; } void g(User& v) { //(C) v.name = "Yukon"; //(D) v.age = 200; //(E) }

As a result, the cout statement in main will print out the state of the User object showing the changes made in the body of g.

[1]Implicit type conversions for assignment of arguments to parameters in a function call are discussed in Chapter 7.

[2]Strictly speaking, the "pass by pointer" is just another version of "pass by value," the only difference being that the argument being passed is a memory address. Although that's technically true, it's still useful to make a distinction between the two modes of argument passing, because with "pass by pointer" a function can be made to do useful things to the argument object through side effects. On the other hand, "pass by value" does not allow for side effects to alter the state of the argument object.

[3]It is interesting to realize that in this example we could not have written main as

        int main () { g(100); }

With this main, the compiler will compile the program but only after warning of an anachronism. The compiler will complain about the following assignment involved in the matching of the argument with the parameter when g( int& ) is called in main:

       int& y = 100; 

As was mentioned in the previous chapter, such as assignment is an error. The initializer for a T& type must be an object of type T whose address can be ascertained. The constant 100 has no address associated with it. This constraint does not apply to a reference of const T& type. For example, it would be legal to say

       const int& y = 100; 

So with the version of main shown in this footnote, we would need to rewrite g as

       void g(const int& y) { y++; } 




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net