5
C++ A Beginner’s Guide by Herbert Schildt
An object can be passed to a function in the same way as any other data type. Objects are passed to
functions using the normal C++ call-by-value parameter-passing convention. This means that a copy of
the object, not the actual object itself, is passed to the function. Therefore, changes made to the object
inside the function do not affect the object used as the argument to the function. The following program
illustrates this point:
The output is shown here:
Value of a before calling change(): 10
6
C++ A Beginner’s Guide by Herbert Schildt
Value of ob inside change(): 100
Value of a after calling change(): 10
As the output shows, changing the value of ob inside change( ) has no effect on a inside main( ).
Constructors, Destructors, and Passing Objects
Although passing simple objects as arguments to functions is a straightforward procedure, some rather
unexpected events occur that relate to constructors and destructors. To understand why, consider this
short program:
This program produces the following unexpected output:
7
C++ A Beginner’s Guide by Herbert Schildt
As you can see, there is one call to the constructor (which occurs when a is created), but there are two
calls to the destructor. Let’s see why this is the case.
When an object is passed to a function, a copy of that object is made. (And this copy becomes the
parameter in the function.) This means that a new object comes into existence. When the function
terminates, the copy of the argument (that is, the parameter) is destroyed. This raises two fundamental
questions: First, is the object’s constructor called when the copy is made? Second, is the object’s
destructor called when the copy is destroyed? The answers may, at first, surprise you.
When a copy of an argument is made during a function call, the normal constructor is not called.
Instead, the object’s copy constructor is called. A copy constructor defines how a copy of an object is
made. (Later in this module you will see how to create a copy constructor.)
However, if a class does not explicitly define a copy constructor, then C++ provides one by default. The
default copy constructor creates a bitwise (that is, identical) copy of the object.
The reason a bitwise copy is made is easy to understand if you think about it. Since a normal constructor
is used to initialize some aspect of an object, it must not be called to make a copy of an already existing
object. Such a call would alter the contents of the object. When passing an object to a function, you
want to use the current state of the object, not its initial state.
However, when the function terminates and the copy of the object used as an argument is destroyed,
the destructor function is called. This is necessary because the object has gone out of scope. This is why
the preceding program had two calls to the destructor. The first was when the parameter to display( )
went out of scope. The second is when a inside main( ) was destroyed when the program ended.
To summarize: When a copy of an object is created to be used as an argument to a function, the normal
constructor is not called. Instead, the default copy constructor makes a bit-by-bit identical copy.
However, when the copy is destroyed (usually by going out of scope when the function returns), the
destructor is called.
Passing Objects by Reference
Another way that you can pass an object to a function is by reference. In this case, a reference to the
object is passed, and the function operates directly on the object used as an argument. Thus, changes
made to the parameter will affect the argument, and passing an object by reference is not applicable to
all situations. However, in the cases in which it is, two benefits result. First, because only an address to
the object is being passed rather than the entire object, passing an object by reference can be much
faster and more efficient than passing an object by value. Second, when an object is passed by
8
C++ A Beginner’s Guide by Herbert Schildt
reference, no new object comes into existence, so no time is wasted constructing or destructing a
temporary object.
Here is an example that illustrates passing an object by reference:
The output is
9
C++ A Beginner’s Guide by Herbert Schildt
In this program, both display( ) and change( ) use reference parameters. Thus, the address of the
argument, not a copy of the argument, is passed, and the functions operate directly on the argument.
For example, when change( ) is called, a is passed by reference. Thus, changes made to the parameter
ob in change( ) affect a in main( ). Also, notice that only one call to the constructor and one call to the
destructor is made. This is because only one object, a, is created and destroyed. No temporary objects
are needed by the program.
A Potential Problem When Passing Objects
Even when objects are passed to functions by means of the normal call-by-value parameter-passing
mechanism, which, in theory, protects and insulates the calling argument, it is still possible for a side
effect to occur that may affect, or even damage, the object used as an argument. For example, if an
object allocates some system resource (such as memory) when it is created and frees that resource
when it is destroyed, then its local copy inside the function will free that same resource when its
destructor is called. This is a problem because the original object is still using this resource. This situation
usually results in the original object being damaged.
One solution to this problem is to pass an object by reference, as shown in the preceding section. In this
case, no copy of the object is made, and thus, no object is destroyed when the function returns. As
explained, passing objects by reference can also speed up function calls, because only the address of the
object is being passed. However, passing an object by reference may not be applicable to all cases.
Fortunately, a more general solution is available: you can create your own version of the copy
constructor. Doing so lets you define precisely how a copy of an object is made, allowing you to avoid
the type of problems just described. However, before examining the copy constructor, let’s look at
another, related situation that can also benefit from a copy constructor.
CRITICAL SKILL 9.4: Returning Objects
Just as objects can be passed to functions, functions can return objects. To return an object, first declare
the function as returning a class type. Second, return an object of that type using the normal return
statement. The following program has a member function called mkBigger( ). It returns an object that
gives val a value twice as large as the invoking object.
10
C++ A Beginner’s Guide by Herbert Schildt
11
C++ A Beginner’s Guide by Herbert Schildt
12
C++ A Beginner’s Guide by Herbert Schildt
In this example, mkBigger( ) creates a local object called o that has a val value twice that of the invoking
object. This object is then returned by the function and assigned to a inside main( ). Then o is destroyed,
causing the first “Destructing” message to be displayed. But what explains the second call to the
destructor?
When an object is returned by a function, a temporary object is automatically created, which holds the
return value. It is this object that is actually returned by the function. After the value has been returned,
this object is destroyed. This is why the output shows a second “Destructing” message just before the
message “After mkBigger( ) returns.” This is the temporary object being destroyed.
As was the case when passing an object to a function, there is a potential problem when returning an
object from a function. The destruction of this temporary object may cause unexpected side effects in
some situations. For example, if the object returned by the function has a destructor that releases a
resource (such as memory or a file handle), that resource will be freed even though the object that is
assigned the return value is still using it. The solution to this type of problem involves the use of a copy
constructor, which is described next.
One last point: It is possible for a function to return an object by reference, but you need to be careful
that the object being referenced does not go out of scope when the function is terminated.
1. Constructors cannot be overloaded. True or false?
13
C++ A Beginner’s Guide by Herbert Schildt
2. When an object is passed by value to a function, a copy is made. Is this copy destroyed when the
function returns?
3. When an object is returned by a function, a temporary object is created that contains the return
value. True or false?
CRITICAL SKILL 9.5: Creating and Using a Copy Constructor
As earlier examples have shown, when an object is passed to or returned from a function, a copy of the
object is made. By default, the copy is a bitwise clone of the original object. This default behavior is
often acceptable, but in cases where it is not, you can control precisely how a copy of an object is made
by explicitly defining a copy constructor for the class. A copy constructor is a special type of overloaded
constructor that is automatically invoked when a copy of an object is required.
To begin, let’s review why you might need to explicitly define a copy constructor. By default, when an
object is passed to a function, a bitwise (that is, exact) copy of that object is made and given to the
function parameter that receives the object. However, there are cases in which this identical copy is not
desirable. For example, if the object uses a resource, such as an open file, then the copy will use the
same resource as does the original object. Therefore, if the copy makes a change to that resource, it will
be changed for the original object, too!
Furthermore, when the function terminates, the copy will be destroyed, thus causing its destructor to be
called. This may cause the release of a resource that is still needed by the original object.
A similar situation occurs when an object is returned by a function. The compiler will generate a
temporary object that holds a copy of the value returned by the function. (This is done automatically
and is beyond your control.) This temporary object goes out of scope once the value is returned to the
calling routine, causing the temporary object’s destructor to be called. However, if the destructor
destroys something needed by the calling code, trouble will follow.
At the core of these problems is the creation of a bitwise copy of the object. To prevent them, you need
to define precisely what occurs when a copy of an object is made so that you can avoid undesired side
effects. The way you accomplish this is by creating a copy constructor.
Before we explore the use of the copy constructor, it is important for you to understand that C++
defines two distinct types of situations in which the value of one object is given to another. The first
situation is assignment. The second situation is initialization, which can occur three ways:
When one object explicitly initializes another, such as in a declaration
When a copy of an object is made to be passed to a function
When a temporary object is generated (most commonly, as a return value)
14
C++ A Beginner’s Guide by Herbert Schildt
The copy constructor applies only to initializations. The copy constructor does not apply to assignments.
The most common form of copy constructor is shown here:
classname (const classname &obj) {
// body of constructor }
Here, obj is a reference to an object that is being used to initialize another object. For example,
assuming a class called MyClass,and y as an object of type MyClass, then the following statements would
invoke the MyClass copy constructor:
MyClass x = y; // y explicitly initializing x func1(y); // y passed as a parameter y = func2(); // y receiving a returned
object
In the first two cases, a reference to y would be passed to the copy constructor. In the third, a reference
to the object returned by func2( ) would be passed to the copy constructor. Thus, when an object is
passed as a parameter, returned by a function, or used in an initialization, the copy constructor is called
to duplicate the object.
Remember, the copy constructor is not called when one object is assigned to another. For example, the
following sequence will not invoke the copy constructor:
MyClass x; MyClass y;
x = y; // copy constructor not used here.
Again, assignments are handled by the assignment operator, not the copy constructor.
The following program demonstrates a copy constructor:
Không có nhận xét nào:
Đăng nhận xét