In the accompanying article, C++ Programming Basics - Creating a smart pointer template class we describe a problem you could face if you created a smart pointer object from another smart pointer object of the same type. Both smart pointers will address the same block of memory and then try to delete that block of memory when they leave scope.
As you probably know, calling delete on the same pointer
twice may yield unpredictable resultsin some cases the
program may crash. Therefore, you may want to make it illegal
for you or another programmer to copy one smart pointer's
data to another.
In general, there are two situations you'll want to guard against: initializing a new smart pointer with an existing one and assigning one smart pointer to another. The difference between these two situations is important, but not always obvious.
For example, if you write these statements in your code
employee bob; employee fred = bob;
the compiler will call the employee class's copy
constructor to initialize the employee object fred.
This is functionally equivalent to writing
employee fred(bob);
which calls the employee class's copy constructor explicitly.
On the other hand, if you write the statements
employee bob; employee fred; employee fred = bob;
the compiler will first use the default constructor to create the employee object fred and then call the employee class's assignment operator to initialize the object fred with the data from the object bob.
As we mentioned in the accompanying article, the compiler will
provide default versions of these functions for your class if
you don't declare them. The default version of the copy
constructor the compiler creates is equivalent to
employee::employee(const employee& e) { memcpy(name, e.name, 20); hourlyRate = hourlyRate; }
The body of the assignment operator is similar.
For each data member of a class or struct, the compiler will perform
a member-by-member copy to set the values in the target object.
If a given member is a user-defined type, the compiler will call
the copy constructor for that type. If, instead, the data member
is a simple type (int, float, char,
and so on), the compiler will perform a bitwise copy of that member.
Even if you don't assign or initialize objects with statements similar to those we've shown above, your program may still try to call the class's copy constructor. When the compiler needs to generate a temporary object from a current object, it will call the copy constructor of the object's class.
Typically, the compiler will create temporary objects whenever
you pass or return an object by value. For example, if you write
a function
printEmployeePayRate(employee emp) { cout << e.hourlyRate << endl; }
the compiler will create a temporary value for the parameter emp
and then call the copy constructor for the employee class
to initialize it.
There are two ways you can avoid problems with copying objects that shouldn't share data. First, you can implement deep copying. That is, you can write your own versions of the copy constructor and assignment operator functions to copy via a pointer the data that would have been shared.
Second, if copying the shared data isn't appropriate for
your application, you can declare both functions but not provide
a function body. If you do this, you'll prevent the compiler
from creating the default versions of these functions, and the
linker will display an error if any module in the application
tries to call either of them.
Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.