Copy Control (Part II)
• Review: copy control consists of 5 distinct operations
– A copy constructor initializes an object by duplicating
the const l-value that was passed to it by reference
– A copy-assignment operator (re)sets an object’s value
by duplicating the const l-value passed to it by reference
– A destructor manages the destruction of an object
– A move constructor initializes an object by transferring
the implementation from the r-value reference passed to it
– A move-assignment operator (re)sets an object’s value
by transferring the implementation from the r-value reference
passed to it
• Today we’ll focus on the last 2 operations and other
features (introduced in C++11) like r-value references
– I.e., features that support the new C++11 move semantics
CSE 332: C++ copy control II
Motivation for Move Semantics
• Copy construction and copy-assignment may be
expensive due to time/memory for copying
• It would be more efficient to simply “take” the
implementation from the passed object, if that’s ok
• It’s ok if the passed object won’t be used afterward
– E.g., if it was passed by value and so is a temporary object
– E.g., if a special r-value reference says it’s ok to take from
(as long as object remains in a state that’s safe to destruct)
• Note that some objects require move semantics
– I.e., types that don’t allow copy construction/assignment
– E.g., unique_ptr, ifstream, thread, etc.
• New for C++11: r-value references and move function
– E.g., int i; int &&rvri = std::move(i);
CSE 332: C++ copy control II
Synthesized Move Operations
• Compiler will only synthesize a move operation if
– Class does not declare any copy control operations, and
– Every non-static data member of the class can be moved
• Members of built-in types can be moved
– E.g., by std::move etc.
• User-defined types that have synthesized/defined
version of the specific move operation can be moved
• L-values are always copied, r-values can be moved
– If there is no move constructor, r-values only can be copied
• Can ask for a move operation to be synthesized
– I.e., by using = default
– But if cannot move all members, synthesized as = delete
CSE 332: C++ copy control II
R-Values, L-Values, and References to Either
• A variable is an l-value (has a location)
– E.g., int i = 7;
• Can take a regular (l-value) reference to it
– E.g., int & lvri = i;
• An expression is an r-value
– E.g., i * 42
• Can only take an r-value reference to it (note syntax)
– E.g., int && rvriexp = i * 42;
• Can only get r-value reference to l-value via move
– E.g., int && rvri = std::move(i);
– Promises that i won’t be used for anything afterward
– Also, must be safe to destroy i (could be stack/heap/global)
CSE 332: C++ copy control II
Move Constructors
• Note r-value reference
// takes implementation from a
IntArray::IntArray(IntArray &&a)
: size_(a.size_),
values_(a.values_) {
– Says it’s safe to take a’s
implementation from it
– Promises only subsequent
operation will be destruction
• Note constructor design
// make a safe to destroy
a.values_ = nullptr;
a.size_ = 0;
}
CSE 332: C++ copy control II
– A lot like shallow copy
constructor’s implementation
– Except, zeroes out state of a
– No sharing, current object
owns the implementation
– Object a is now safe to destroy
(but is not safe to do anything
else with afterward)
Move-Assignment Operators
• No allocation, so no exceptions to worry about
– Simply free existing implementation (delete values_)
– Then copy over size and pointer values from a
– Then zero out size and pointer in a
• This leaves assignment complete, a safe to destroy
– Implementation is transferred from a to current object
Array & Array::operator=(Array &&a) { // Note r-value reference
if (&a != this) { // still test for self-assignment
delete [] values_;
// safe to free first (if not self-assigning)
size_ = a. size_;
// take a’s size value
values_ = a.values_; // take a’s pointer value
a.size_ = 0;
// zero out a’s size
a.values_ = nullptr; // zero out a’s pointer (now safe to destroy)
}
return *this;
}
CSE 332: C++ copy control II
Move Semantics and Inheritance
• Base classes should declare/define move operations
– If it makes sense to do so at all
– Derived classes then can focus on moving their members
– E.g., calling Base::operator= from Derived:: version
• Containers further complicate these issues
– Containers hold their elements by value
– Risks slicing, other inheritance and copy control problems
• So, put (smart) pointers, not objects, into containers
– Access is polymorphic if destructors, other methods virtual
– Smart pointers may help reduce need for copy control
operations, or at least simplify cases where needed
CSE 332: C++ copy control II
© Copyright 2026 Paperzz