- Locality of Declaration
- Returning a Class Object
- Initialization versus Assignment of Class Objects
Initialization versus Assignment of Class Objects
Now that we understand the general transformation of a function that returns a class object by value, we are able to return to the question of why the initialization of a class object is generally less expensive than its assignment.
When we write
Matrix composite = m1 + m2;
the compiler must internally transform this to match the internal transformation of the Matrix addition operator. One possible transformation is the following:
// Psuedo C++ Code // One possible internal transformation of // Matrix composite = m2 + m2 Matrix temp; operator+( temp, m1, m2 ); Matrix composite( temp ); temp.Matrix::~Matrix();
In order for this to work, the definition of
Matrix temp; // no associated constructor call
must not be followed by an invocation of the default constructor. (Because the compiler generates the temporary, it can suppress the constructor invocation.) This is necessary, remember, because temp is constructed within the addition operator, and we cannot initialize an object twice. temp, that is, must represent raw storage.
Although this transformation is semantically correct, it can be considerably improved when the compiler recognizes that composite also represents raw storage. That is, rather than generating a temporary to copy construct into composite, the compiler can pass composite directly into the addition operator:
// Psuedo C++ Code // A more aggressive internal transformation Matrix composite; operator+( composite, m1, m2 );
Again, this is possible because composite, at this point, represents raw storage. (The internal definition of composite, above, only allocates storage; the compiler suppresses an invocation of the default Matrix constructor, the same as it did when it defined temp.)
What about the assignment?
composite = m1 + m2;
composite, at this point, represents initialized storage. It cannot be directly passed into the addition operator, which would result in a second initialization. Rather, the compiler must generate raw storage and then copy assign the result to composite:
// Psuedo C++ Code // internal transformation of assignment Matrix temp; operator+( temp, m1, m2 ); composite.Matrix::operator=( temp ); temp.Matrix::~Matrix();
In general, assignment requires the generation and destruction of a temporary class object. Under initialization, the temporary is unnecessary; the actual object being defined can be used directly. Armed with this knowledge, do you see the inefficiency in the following loop?
Matrix mat; while ( something.more() ) { mat = something.fetch_mat(); // do something with mat ... }
mat is set to a different Matrix class object with each iteration. The programmer could have written
while ( something.more() ) { Matrix mat = something.fetch_mat(); // do something with mat ... }
but wished to avoid the construction and destruction of mat with each loop iteration. Let's rather initialize and destroy it once prior to and after completion of the loop, the programmer explains, not realizing that with each iteration, the assignment requires that a temporary be passed to fetch_mat(), where it is constructed. The temporary is then copy assigned to mat and destructed. While placing the definition of mat outside the loop appears more efficient, it is actually more expensive, resulting in an additional copy assignment operation with each loop iteration, in addition to the construction and destruction of a temporary Matrix object.