Copy and move
An object that owns memory can be copied or moved. A copy makes a new block and copies every element into it, so the two objects are independent. A move hands the existing block to the new object and leaves the old one empty, so nothing is copied. Follow a std::vector through one copy, one move and the end of its scope, then see the rule of five and the rule of zero.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01After std::vector<int> a{1, 2, 3}; auto b = a; b[0] = 9; what does a hold?
auto b = a; runs the copy constructor, which gives b its own new block with copies of the elements. Changing b's block never changes a's.
02What does std::move(a) do on its own?
std::move is only a cast. The actual moving happens in the move constructor (or move assignment) that the cast lets the compiler pick, as in auto c = std::move(a);.
03Why is moving a vector of a million elements cheap?
The move constructor takes the source's pointer and leaves the source empty. That is constant time; a copy is linear in the number of elements.
04After auto c = std::move(a); with a std::vector a, which use of a is safe?
A moved-from vector is empty, and moved-from standard library objects in general are valid but unspecified. You may assign a new value or let the object be destroyed; do not rely on its old contents.
05A class Buf holds an int* to a block it allocates with new[]. Which members should it define (or delete)?
That is the rule of five. The compiler's copy would copy only the pointer, so two objects would free the same block twice. The simpler route is the rule of zero: hold the data in a std::vector or std::unique_ptr member instead.
Glossary
- Copy constructor
- The constructor that makes a new object from an existing one of the same type, written T(const T&). For an owning type it makes a deep copy.
- Deep copy
- A copy that duplicates the owned memory too, so the two objects share nothing and can change independently.
- Move constructor
- The constructor that makes a new object by taking over another object's resources, written T(T&&). It leaves the source valid but usually empty.
- std::move
- A standard function from <utility> that casts its argument to an rvalue reference. It moves nothing by itself.
- Rvalue reference
- A reference written T&& that binds to temporaries and to objects passed through std::move: a signal that their resources may be taken.
- Moved-from object
- An object whose resources were taken by a move. It must still be safe to destroy or assign to; standard library objects are left valid but unspecified.
- Rule of five
- If a class needs a custom destructor, copy constructor, copy assignment, move constructor or move assignment, it almost always needs all five.
- Rule of zero
- Design classes so they need none of the five: let members such as std::vector or std::unique_ptr manage the resources.
Notes and sources
- The figure draws a vector as a pointer and a size. Real libraries (libstdc++, libc++, Microsoft's STL) keep three pointers: the start of the elements, their end, and the end of the reserved space. The idea is the same.
- "Heap" is the everyday name. The C++ standard calls it dynamic storage (the free store): memory obtained with new or through an allocator, as std::vector does.
- cppreference: unless stated otherwise, standard library objects that have been moved from are left in a valid but unspecified state. For std::vector's move constructor the source is guaranteed to be empty afterwards.
- cppreference lists std::vector's copy constructor as linear in the size of the other vector and its move constructor as constant.
- Mark move constructors noexcept (C++ Core Guidelines C.66). When a std::vector grows it moves its elements only if their move constructor cannot throw; otherwise it copies them, to keep its strong exception guarantee.
- Initialising from a temporary, or returning a local object, often skips the copy or move entirely: this is copy elision, guaranteed for temporaries since C++17.
- The card's five lines are the body of main in a complete program that includes <iostream>, <utility> and <vector>. It compiles without warnings with g++ -std=c++20 -Wall -Wextra and prints 0.
- cppreference.com: Copy constructors
- cppreference.com: Move constructors
- cppreference.com: std::move
- cppreference.com: std::vector constructors (complexity, state after a move)
- cppreference.com: Destructors
- cppreference.com: The rule of three/five/zero
- cppreference.com: std::move_if_noexcept
- cppreference.com: Copy elision
- Standard C++ Foundation (isocpp.org): C++11 FAQ, rvalue references and move semantics
- C++ Core Guidelines (isocpp.github.io): C.20, C.21 and C.66
Quiz Unlocks after the last step.