Passing structs to Functions

struct variables can be used like any other variable type. If p1 and p2 are both of type Person, then p1 = p2 will copy all the members of p2 to p1. struct variables can be passed as arguments to functions; by default they are passed by value. The function int getx(Point p) { return p.x; } returns the x coordinate of a Point p. The function call getx(p1) will copy the actual argument (p1) into the formal argument (p). It is also possible to pass a struct by reference. Such arguments can be very convenient ways to pass a lot of information to a function. Functions may also return struct values.

Using Structures Instead of Many Parameters

Functions that have too many arguments are not only difficult to type but easy to get wrong. For example, read_any_stock() (discussed in Chapter 3, “Arrays and Algorithms”) has five arguments, and it's easy to mix up the prices. The following function compares dates; it is similar to int compare_dates(string d1, string d2) as discussed in that case study, so I have not written it out fully:

int compare_dates(int y1, int m1, int d1,
                   int y2, int m2, int d2)
{
  int cmp = y2 - y1;
  if (cmp != 0) return cmp;
  ...
}

When I call such a function, I easily get confused about the order of the arguments. I know the function needs two dates, but is the day before the month (as I usually write dates)? The Date struct makes this kind of function easier to use, especially if you write a special function date() for making Date objects. Look at the next definition of compare_dates():

Date date(int y, int m, int d) {
  Date dd;
  dd.year = y;  dd.month = m;  dd.day = d;
  return dd;
}
int compare_dates(Date d1, Date d2)
{
  int cmp = d2.year - d1.year;
  ....
}
;> compare_dates(date(2001,07,17),date(2001,06,05));
(int) -1

The function now has only two arguments; note that the function date() and type Date have distinct names.

NOTE

You may have noticed that I write structs and typedef names with an initial capital letter. This is part of the naming convention I like to use; any type names are WrittenLikeThis, and any variables or functions are written_like_this. The only exceptions are the standard library types (such as std::list) and when linking to someone else's functions, such as the Windows API (e.g. GetWindowText()).


Using the Date struct has some advantages over using a string representation. First, it is more compact. The struct can be packed into 4 bytes (two bytes for the year and one each for month and for day). Second, it is much more efficient in terms of speed and code size. Finally, it is automatically in the right format (year,month,day) unlike a date string. A person may easily pass “20 June 2001” to a function expecting “2001-06-20”. A Date variable may still may be invalid, of course (e.g. month is greater than 12).

Passing Structures by Value

Remember from Chapter 3, “Arrays and Algorithms,” that vectors are always passed by value. The same applies to user-defined structures. This means two things: You don't have to worry about accidentally modifying a passed structure, and you do have to sometimes worry about the cost of all that copying. A function can also return a structure, but it isn't a good idea for large structures such as Person, which is introduced earlier in this chapter. For compact structures such as Point, you can use this fact to make a function that constructs points, as in the following example:


void show_point(Point p) {
 cout << p.x << ' ' << p.y << endl;
}
Point make_point(int a,int b) {
  Point p;
  p.x = a;  p.y = b;
  return p;
}
;> Point pp = make_point(2,3);
;> show_point(pp);
2 3
;> make_point(5,7).x;
(int) 5

Note that you can follow any expression (such as a call to make_point()) that returns a structure with the dot operator.

Reference Types

In the section “Containers,” of Chapter 3, I mentioned that you can force an argument to be passed by reference by using the address-of operator (&) in the argument type. This operator means that the argument has a reference type. Reference types can be declared anywhere, but they must be initialized with a proper variable, as in the following example:

;> int& ri;
CON 3:uninitialized reference
;> int k = 1;
;> int& kr = k;
;> kr;
(int&) 1
;> kr = 2;
(int&) 2
;> k;
(int) k = 2

The reference kr is, in effect, just another name for k; whatever you do to kr will affect k and vice versa. This is because they occupy the same address in memory: kr is an alias for k (that is, another name for the same thing). References are often used when a function has to pass more than one item of information back to the caller. The function modify_ij() is intended to return two integer values; by passing i and j as references, they can both be modified:

bool modify_ij (int& i, int& j) {
   i = 0; j = 0;
   return true;
 }
; int m = 9;
;> modify_ij(k,m);
(bool) true
;> k; m;
(int) k = 0
(int) m = 9

A function may return a reference, but there can be problems. If you are returning a reference to a local variable, then by the time the function has returned to its caller, that local variable may no longer exist. For instance, if you declared the return type of make_point() as Point&, you might get very strange results. Returning references may work on one machine, but not on another.

const References

Passing a struct variable as a reference is usually fastest for a nontrivial structure, but this allows the structure to be modified by the function. You use the const qualifier to insist that an argument be passed as a reference to a constant object, as in the following example:

;> int var = 1;
;> const int& var_ref = var;
;> var_ref = 2;  // error! Can't modify a const reference!
CON 23:Can't assign to a const type
;> string fullname(const Person& p) {  return p.first_name + " " + p.last_name; }
						

You should declare the function compare_date(const Date& d1, const Date& d2) rather than compare_date(Date d1, Date d2). In the case of compare_date() there is probably little difference; Date would be packed into a 4-byte word (but it might end up as three words if the compiler decides to use at least one word per field—this is called word alignment). Large structures such as Person would be expensive to pass by value. You should qualify a reference with const, unless you intend to modify the value. Experienced programmers assume that an object will be modified if they don't see const.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.255.187