A copy constructor is invoked whenever a new object is created and initialized to an existing object of the same kind. This happens in several situations. The most obvious situation is when you explicitly initialize a new object to an existing object. For example, given that motto is a StringBad object, the following four defining declarations invoke a copy constructor:
StringBad ditto(motto); // calls StringBad(const StringBad &)
StringBad metoo = motto; // calls StringBad(const StringBad &)
StringBad also = StringBad(motto);
// calls StringBad(const StringBad &)
StringBad * pStringBad = new StringBad(motto);
// calls StringBad(const StringBad &)
Depending on the implementation, the middle two declarations may use a copy constructor directly to create metoo and also, or they may use a copy constructor to generate temporary objects whose contents are then assigned to metoo and also. The last example initializes an anonymous object to motto and assigns the address of the new object to the pstring pointer.
Less obviously, a compiler uses a copy constructor whenever a program generates copies of an object. In particular, it’s used when a function passes an object by value (as callme2() does in Listing 12.3) or when a function returns an object. Remember, passing by value means creating a copy of the original variable. A compiler also uses a copy constructor whenever it generates temporary objects. For example, a compiler might generate a temporary Vector object to hold an intermediate result when adding three Vector objects. Compilers vary as to when they generate temporary objects, but all invoke a copy constructor when passing objects by value and when returning them. In particular, this function call in Listing 12.3 invokes a copy constructor:
callme2(headline2);
The program uses a copy constructor to initialize sb, the formal StringBad-type parameter for the callme2() function.
By the way, the fact that passing an object by value involves invoking a copy constructor is a good reason for passing by reference instead. That saves the time of invoking the constructor and the space for storing the new object.
What a Default Copy Constructor Does
The default copy constructor performs a member-by-member copy of the nonstatic members (
StringBad sailor = sports;
amounts to the following (aside from the fact that it doesn’t compile because access to private members is not allowed):
StringBad sailor;
sailor.str = sports.str;
sailor.len = sports.len;
If a member is itself a class object, the copy constructor for that class is used to copy one member object to another. Static members, such as num_strings, are unaffected because they belong to the class as a whole instead of to individual objects. Figure 12.2 illustrates the action of an implicit copy constructor.
Figure 12.2. An inside look at memberwise copying.
Back to Stringbad: Where the Copy Constructor Goes Wrong
You are now in a position to understand the twofold weirdness of Listing 12.3. (Let’s assume that the output is the one shown after the listing.) The first weirdness is that the program output indicates two more objects destroyed than constructed. The explanation is that the program does create two additional objects, using the default copy constructor. The copy constructor is used to initialize the formal parameter of callme2() when that function is called, and it is used to initialize the object sailor to sports. The default copy constructor doesn’t vocalize its activities, so it doesn’t announce its creations, and it doesn’t increment the num_strings counter. However, the destructor does update the count, and it’s invoked upon the demise of all objects, regardless of how they were constructed. This weirdness is a problem because it means the program doesn’t keep an accurate object count. The solution is to provide an explicit copy constructor that does update the count:
StringBad::StringBad(const String & s)
{
num_strings++;
...// important stuff to go here
}
Tip
If your class has a static data member whose value changes when new objects are created, you should provide an explicit copy constructor that handles the accounting.
The second weirdness is the more subtle and dangerous. One symptom is the garbled string contents:
headline2: Dû°
The cause is that the implicit copy constructor copies by value. Consider Listing 12.3, for example. The effect, recall, is this:
sailor.str = sport.str;