1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
Once a class becomes a emi(move-aware) class one should realize that its
destructor still performs its job as implemented. Consequently, when moving
pointer values from a temporary source to a destination the move constructor
commonly ensures that the temporary object's pointer value is set to zero, to
prevent doubly freeing memory.
If a class defines pointers to pointer data members there usually is not
only a pointer that is moved, but also a tt(size_t) defining the number of
elements in the array of pointers.
Once again, consider the class tt(Strings). Its destructor is implemented
like this:
verb( Strings::~Strings()
{
for (string **end = d_string + d_size; end-- != d_string; )
delete *end;
delete[] d_string;
})
The move constructor (and other move operations!) must realize that the
destructor not only deletes tt(d_string), but also considers tt(d_size). When
tt(d_size) and tt(d_string) are set to 0, the destructor (correctly) won't
delete anything. In addition, when the class uses
capacity-doubling once tt(d_size ==
d_capacity) then the move constructor can still reset the source's
(d_capacity) to 0, since it's em(known) that the tt(tmp) object ceases to
exist following the move-assignment:
verb( Strings::Strings(Strings &&tmp)
:
d_string(tmp.d_string),
d_size(tmp.d_size),
d_capacity(tmp.d_capacity)
{
tmp.d_string = 0;
tmp.d_capacity = 0;
tmp.d_size = 0;
})
Other variations are possible as well. The bottom line: the move construcor
must ensure that after the destination object has grabbed the source object's
data the source object remains in a valid state. That's easily accomplished by
assigning the same values to its data members as set by the default
constructor.
|