Construction Versus Assignment
In the previous sections, we saw some examples of constructors and assignment operators at work. The difference between construction, sometimes called initialization, and assignment can be a subtle and confusing point for C++ beginners because within the C++ language they can share similar syntax. Consider the following case:
RWMathVec<double> a(10,1), b(10,2), d(10,3);
.
.
.
RWMathVec<double> c = a + b; // This is initialization
d = a + b; // This is assignment
In this example, the variable c is initialized using the copy constructor:
RWMathVec<T>::RWMathVec<T>(const RWMathVec<T>&);
while the variable d is being assigned using the assignment operator:
RWMathVec<T>& RWMathVec<T>::operator=(const RWMathVec<T>&);
The two have very different meanings. The key points to remember are:
Construction and assignment can have similar syntax, but are treated separately by the compiler. This is part of the language. They
may or
may not have similar semantics or meanings, depending on the design of the classes.
Construction of an Essential Math Module object
from another Essential Math Module object via the copy constructor copies the object's view, but not the object's data.
Assignment of an Essential Math Module object to another object
or to a scalar always causes the contents of the right-hand side to be
copied over to the left-hand side's data. The left-hand side's data is changed, but not its view. Assignment is a
conformal operator; the two sides must have the same number of elements or a runtime error will occur.
Since the copy constructor simply creates a new view without creating new data, both the Essential Math Module objects reference the same data after construction. Although it may seem like using two names for the same data can cause confusion, there are good reasons for doing so. Generally, this aliasing property is not a problem and can even be used to good advantage.
Note that these properties are peculiar to the Essential Math Module classes; there is nothing in the C++ language that says that things must be done this way. The reasons that we choose to do things this way are speed—referencing old data with a new view is much faster than copying it—and flexibility. As we'll describe later, aliasing with multiple views allows subscripting to extract and operate on parts of arrays in a simple, efficient way. The reason why the aliasing property is seldom a problem is that initialization via the copy constructor is most often used when temporaries are involved. Consider this code excerpt:
RWMathVec<double> a(10,1), b(10,2);
.
.
.
RWMathVec<double> c = a + b;
The addition of a and b generates a temporary, created by the compiler. It never takes on a name and it is destroyed at the first opportunity, so no confusion results when vector c views its data.
Another common example of copy construction is when objects are returned from functions:
RWMathVec<double> foo(int N)
{
RWMathVec<double> d(N);
.
.
.
return d; // Return an RWMathVec<double>
}
In this example, the vector d exists inside the function foo. To get it outside requires the initialization of a temporary with d as an argument. This temporary is what is actually used by the calling program. Once foo is done, d goes out of scope and is destroyed. Hence, there is once again only one view of the data and no confusion.
However, the code:
RWGenMat<double> a;
RWGenMat<double> b = a;
has the potential to be confusing. The matrices a and b both reference the same data, but have different names. It is also difficult for a compiler to optimize this kind of situation. This kind of construct should be avoided.
As you can imagine, initialization is much faster than assignment, which involves copying a lot of elements across the equal sign. Hence, it is much preferred.
Generally, it is better to construct new variables than to keep using old ones.