Bitcopy versus initialization
So far, so good. There’s a workable
process for passing and returning large simple structures. But notice that all
you have is a way to copy the bits from one place to another, which certainly
works fine for the primitive way that C looks at variables. But in C++ objects
can be much more sophisticated than a patch of bits; they have meaning. This
meaning may not respond well to having its bits copied.
Consider a simple example: a class that
knows how many objects of its type exist at any one time. From Chapter 10, you
know the way to do this is by including a static data
member:
//: C11:HowMany.cpp
// A class that counts its objects
#include <fstream>
#include <string>
using namespace std;
ofstream out("HowMany.out");
class HowMany {
static int objectCount;
public:
HowMany() { objectCount++; }
static void print(const string& msg = "") {
if(msg.size() != 0) out << msg << ": ";
out << "objectCount = "
<< objectCount << endl;
}
~HowMany() {
objectCount--;
print("~HowMany()");
}
};
int HowMany::objectCount = 0;
// Pass and return BY VALUE:
HowMany f(HowMany x) {
x.print("x argument inside f()");
return x;
}
int main() {
HowMany h;
HowMany::print("after construction of h");
HowMany h2 = f(h);
HowMany::print("after call to f()");
} ///:~
The class HowMany contains a
static int objectCount and a static member function
print( ) to report the value of that objectCount, along with
an optional message argument. The constructor increments the count each time an
object is created, and the destructor decrements it.
The output, however, is not what you
would expect:
after construction of h: objectCount = 1
x argument inside f(): objectCount = 1
~HowMany(): objectCount = 0
after call to f(): objectCount = 0
~HowMany(): objectCount = -1
~HowMany(): objectCount = -2
After h is created, the object
count is one, which is fine. But after the call to f( ) you would
expect to have an object count of two, because h2 is now in scope as
well. Instead, the count is zero, which indicates something has gone horribly
wrong. This is confirmed by the fact that the two destructors at the end make
the object count go negative, something that should never
happen.
Look at the point inside
f( ), which occurs after the argument is passed by value. This means
the original object h exists outside the function frame, and
there’s an additional object inside the function frame, which is
the copy that has been passed by value. However, the argument has been passed
using C’s primitive notion of bitcopying, whereas the C++ HowMany
class requires true initialization to maintain its integrity, so the default
bitcopy fails to produce the desired effect.
When the local object goes out of scope
at the end of the call to f( ), the destructor is called, which
decrements objectCount, so outside the function, objectCount is
zero. The creation of h2 is also performed using a bitcopy, so the
constructor isn’t called there either, and when h and h2 go
out of scope, their destructors cause the negative values of
objectCount.