The textbook first used the swapping problem to demonstrate the behavior of the assignment operator and that variables can only store one value at a time. It next extended the demonstration to include structure objects. However, swapping the contents of two variables is an authentic sub-problem in its own right, making a swap function useful. The text's third treatment of the swapping problem implements a swap function while focusing on various argument passing mechanisms. Chapter 13 presents a generalized version implemented with templates.
C++ provides three function argument passing mechanism:
pass-by-value,
pass-by-pointer, and
pass-by-reference.
However, not all of the mechanisms are appropriate for implementing the swap function, which must change two variables. If changing one variable was sufficient, the function could use the return operator to complete its task. But the requirement to change two variables implies that the function must change both parameters, implying that whichever passing mechanism is used, it must be an INOUT mechanism - it must allow data to flow into and out of the function through the parameters. This requirement eliminates pass-by-value, leaving pass-by-reference and pass-by-pointer. Furthermore, how (or where) the client program creates the data affects how it calls the function.
The swap function: pass-by-reference.
Pass-by-reference provides a simple, straightforward function call, making it the most frequently used mechanism in this situation. Nevertheless, its implementation has one surprising pitfall. The key concept to understanding C++ references is recognizing that a reference creates an alias, another name for an existing variable.
The driver simulates an application program that must swap two objects, perhaps as part of sorting an array of objects. The function call doesn't require any special syntax or operators. The print function is used unchanged from swap 2.
The swap function implemented with the pass-by-reference, but this version doesn't work. It's easy to believe that assignment compatibility (b) requires the function to make temp a reference variable. Stop for a moment and try to explain to yourself why doing so doesn't work.
This version, which works correctly, also implements the function with pass-by-reference, but it doesn't make temp a reference.
A picture helps explain why version (c) works. The body of the swap function maintains three distinct variables: param1, param2, and temp. The first two are aliases for s1 and s2, respectively, but are still distinct from each other, as represented by the three glasses in the initial problem presentation. The assignment operator copies the contents of one variable to another - like pouring the contents of one glass into another.
A picture also helps explain why version (b) fails. Making temp a reference creates a single variable with three names or aliases: temp, param2, and s2. Essentially, it fails to create the third variable needed for swapping. Consequently, the assignment operation temp = param2 copies temp2 to itself, failing to save the value. The second statement, param2 = param1, overwrites and looses the contents of param2. However, param2 and temp are aliases - different names for the same object -, so the assignment also overwrites the contents of temp. When the function returns, both s1 and s2 store the value originally stored in s1.
swap: Pass-By-Pointer
swapping variables, swap(), pass by pointer, overloaded functions
The swap function: pass-by-pointer (1).
Although the function's basic framework is the same as the previous version, pass-by-pointer (aka pass-by-address) is more complex and requires additional operators. However, it's also more broadly applicable, for example, when programs make system calls.
The driver again simulates an application program that must swap two variables. s1 and s2 are automatic (or local) variables created on the stack. Consequently, the swap function call must find (with the address-of operator) and pass the address of each object.
Adding the asterisks to the swap function's parameters makes them pointer variables. Dereferencing the parameters in the function's body causes the assignment operations to copy the objects (the contents of the variables) themselves, not their addresses. Notice that temp is not a pointer; making it a pointer would cause a problem similar to version (b) above.
int main()
{
student* s1 = new student { 123, "dilbert", 3.0 };
student* s2 = new student { 987, "alice", 4.0 };
print(*s1); // original print
print(*s2);
swap(s1, s2); // pointer version of swap
print(*s1); // original print
print(*s2);
print(s1); // overloaded print
print(s2);
return 0;
}
The swap function: pass-by-pointer (2).
Implementing the swap function with pass-by-pointer makes it flexible, allowing it to swap both stack and heap objects.
The driver simulates an application that creates objects dynamically on the heap. The application continues to use the pointer swap function (Figure 2(b)); s1 and s2 are now pointers, so the application doesn't need to get their address to call the function. The application can also continue to use the original print function by dereferencing the pointers or use an overloaded version that uses pass-by-value or pass-by-reference.
Problem (2) implemented the print function with pass-by-value, as illustrated here. It's also possible to implement it with pass-by-reference, void print(student& temp), without changing its body or the application. However, a program cannot have both versions as it's not possible to overload on pass-by-reference vs. pass-by-value.
It's possible to overload the print function based on pass-by-pointer without conflicting with the other passing mechanisms. The function's parameter becomes a pointer, and it selects the object's fields or members with the arrow operator.