This first draft of a design for StringBad has some deliberate flaws that make the exact output undefined. Some compilers I used, for example, produced versions that aborted before completing. However, although the output details may differ, the basic problems and solutions (soon to be revealed!) are the same.
Here is the output produced after compiling the program in Listing 12.3 with the Borland C++ 5.5 command-line compiler:
Starting an inner block.
1: "Celery Stalks at Midnight" object created
2: "Lettuce Prey" object created
3: "Spinach Leaves Bowl for Dollars" object created
headline1: Celery Stalks at Midnight
headline2: Lettuce Prey
sports: Spinach Leaves Bowl for Dollars
String passed by reference:
"Celery Stalks at Midnight"
headline1: Celery Stalks at Midnight
String passed by value:
"Lettuce Prey"
"Lettuce Prey" object deleted, 2 left
headline2: Dû°
Initialize one object to another:
sailor: Spinach Leaves Bowl for Dollars
Assign one object to another:
3: "C++" default object created
knot: Celery Stalks at Midnight
Exiting the block.
"Celery Stalks at Midnight" object deleted, 2 left
"Spinach Leaves Bowl for Dollars" object deleted, 1 left
"Spinach Leaves Bowl for Doll8" object deleted, 0 left
"@g" object deleted, -1 left
"-|" object deleted, -2 left
End of main()
The various nonstandard characters that appear in the output will vary from system to system; they are one of the signs that StringBad deserves to be called bad. Another sign is the negative object count. Newer compiler/operating system combinations typically abort the program just before displaying the line about having -1 objects left, and some of them report a General Protection Fault (GPF). A GPF indicates that a program tried to access a memory location forbidden to it; this is another bad sign.
Program Notes
The program in Listing 12.3 starts out fine, but it staggers to a strange and ultimately disastrous conclusion. Let’s begin by looking at the good parts. The constructor announces that it has created three StringBad objects, it numbers them, and the program uses the overloaded << operator to list them:
1: "Celery Stalks at Midnight" object created
2: "Lettuce Prey" object created
3: "Spinach Leaves Bowl for Dollars" object created
headline1: Celery Stalks at Midnight
headline2: Lettuce Prey
sports: Spinach Leaves Bowl for Dollars
Then the program passes headline1 to the callme1() function and redisplays headline1 after the call. Here’s the code:
callme1(headline1);
cout << "headline1: " << headline1 << endl;
And here’s the result:
String passed by reference:
"Celery Stalks at Midnight"
headline1: Celery Stalks at Midnight
This section of code seems to work fine, too.
But then the program executes the following code:
callme2(headline2);
cout << "headline2: " << headline2 << endl;
Here, callme2() passes headline2 by value instead of by reference, and the result indicates a serious problem:
String passed by value:
"Lettuce Prey"
"Lettuce Prey" object deleted, 2 left
headline2: Dû°
First, passing headline2 as a function argument somehow causes the destructor to be called. Second, although passing by value is supposed to protect the original argument from change, the function messes up the original string beyond recognition, and some nonstandard characters get displayed. (The exact text displayed will depend on what happens to sitting in memory.)
Even worse, look at the end of the output when the destructor gets called automatically for each of the objects created earlier:
Exiting the block.
"Celery Stalks at Midnight" object deleted, 2 left
"Spinach Leaves Bowl for Dollars" object deleted, 1 left
"Spinach Leaves Bowl for Doll8" object deleted, 0 left
"@g" object deleted, -1 left
"-|" object deleted, -2 left
End of main()
Because automatic storage objects are deleted in an order opposite to that in which they are created, the first three objects deleted are knots, sailor, and sport. The knots and sailor deletions look okay, but for sport, Dollars has become Doll8. The only thing the program does with sport is use it to initialize sailor, but that act appears to have altered sport. And the last two objects deleted, headline2 and headline1, are unrecognizable. Something messes up these strings before they are deleted. Also the counting is bizarre. How can there be -2 objects left?
Actually, the peculiar counting is a clue. Every object is constructed once and destroyed once, so the number of constructor calls should equal the number of destructor calls. Because the object count (num_strings) is decremented two times more than it is incremented, a constructor that doesn’t increment num_strings must be creating two objects. The class definition declares and defines two constructors (both of which increment num_strings), but it turns out that the program uses three constructors! For example, consider this line:
StringBad sailor = sports;