C++ Copy and Move Quiz: Rule of Five Practice Test
Test your C++ copy and move semantics with 14 questions and three programs on copy constructors, destructors, std::move, the rule of five and copy elision.
- Course: C++ study plan
- Module: Copies, moves and the rule of five
- Kind: Checkpoint — cleared at 70%
- Reading time: 28 min
- Runtime: Clang 18 · C++20
Checkpoint — Copies, moves and the rule of five is the checkpoint that closes the Copies, moves and the rule of five module: a graded quiz and whole-program exercises, passed at 70%.
Instructions
This checkpoint covers the whole module: copy construction versus copy assignment, destructors and the order things die, the rule of three and copy-and-swap, rvalue references and std::move, the moved-from state and noexcept, the generation table behind the rule of five and the rule of zero, and value categories with guaranteed elision.
How it works. Fourteen questions and three programs. You need 70% on the questions and every program accepted to clear the module. You can retake it as often as you like; your best score counts.
Before you start, make sure you can answer these from memory:
- Why does the generated copy of a class with an owning raw pointer double-delete?
- In what order are three locals, and the members of one object, destroyed?
- Why does copy-and-swap need no self-assignment check?
- What does
std::movedo, and what does it not do? - Which declarations stop the compiler generating the move operations?
- What state is a
std::vectorin after being moved from, and astd::string? - Why is
return std::move(local);worse thanreturn local;?
The three programs are a fixed-capacity stack with all five special members driven by commands, an event trace through a struct of two probes that you predict before you run, and two shelves passing books between them through std::unique_ptr with no special members at all.
Common questions
Why does copy-and-swap need no self-assignment check?
The parameter is taken by value, so the copy is complete before the body runs. For a = a, a is copied into the parameter, the two are swapped, and the parameter's destructor frees the old block; a keeps its contents, now in a new block, and nothing is read after being freed.
Which declarations stop the compiler generating the move operations?
A user-declared copy constructor, copy assignment operator or destructor — including ones written = default or = delete — or a declaration of one of the move operations. After that, rvalues bind to const T& and are copied unless the moves are declared, for example as = default.
Why is return std::move(local); worse than return local;?
return local; lets the compiler build the local directly in the caller's object (NRVO) or, at worst, move it implicitly. std::move(local) is not a plain name, so NRVO is ruled out and a move is always performed.
What state is a std::vector in after being moved from?
After auto b = std::move(a); the vector a is guaranteed to be empty. A moved-from std::string is only valid but unspecified — usually empty in practice — so a program must not rely on it and should assign a new value before reading it.
Exercises
A stack with the rule of five
Write IntStack, a fixed-capacity stack of int that owns its storage through a raw pointer and implements all five special member functions. Events: the constructor prints allocate <capacity>; the copy constructor deep-copy <size>; the copy assignment copy-assign <size> (or self-assign for itself, doing nothing else); the move constructor move <size>; the move assignment move-assign <size> (or self-assign); the destructor release <capacity>. A copy takes the source's capacity and elements. A moved-from stack has a null pointer, capacity 0 and size 0 — so a push into it reports full, and it releases 0.
Read cap, then k, then k commands operating on two stacks a and b, both constructed with capacity cap (in that order):
push X v— pushes; printsX: fullif there is no room, nothing otherwise;pop X— printsX: popped <v>orX: empty;print X— printsX: size=<n> [<values bottom to top>];copy X Y—X = Y(copy assignment; whenXandYare the same name it is a self-assignment);move X Y—X = std::move(Y);clone X— in a block, copy-constructs a stack fromXand prints it asclone: size=<n> [<values>]; the clone dies at the block's end.
At the end of main the two stacks are destroyed in reverse order.
Input: cap, k, then k command lines. Output: the trace, as below.
2
8
push a 1
push a 2
push a 3
copy b a
pop b
move a b
print a
print b
prints
allocate 2
allocate 2
a: full
copy-assign 2
b: popped 2
move-assign 1
a: size=1 [1]
b: size=0 []
release 0
release 2Predict the trace
Write the five-special-member Probe from lesson 5 (events construct, copy, copy-assign, move, move-assign, destroy; a moved-from value becomes 0), struct Pair { Probe first; Probe second; }; with no special members, Pair makePair(int x, int y) returning Pair{Probe(x), Probe(y)}, and Probe pick(const Pair& p, bool first) returning first ? p.first : p.second by value. Then write this driver and predict its trace before running it:
- Read
x y z. Pair p = makePair(x, y);Probe q = pick(p, x > y);Pair r = p;std::swap(p.first, p.second);—std::swapis one move construction and two move assignments through a temporary, which then dies.r = std::move(p);— the generated move assignment moves the members in declaration order.q = Probe(z);- Print
p=(<first>,<second>) q=<value> r=(<first>,<second>)using thevaluemembers. - Everything dies in reverse order of construction.
Input: three integers. Output: the trace, the values line, and five destroy lines.
1 3 5
prints
construct #1=1
construct #2=3
copy #3 from #2
copy #4 from #1
copy #5 from #2
move #6 from #1
move-assign #1 from #2
move-assign #2 from #6
destroy #6
move-assign #4 from #1
move-assign #5 from #2
construct #7=5
move-assign #3 from #7
destroy #7
p=(0,0) q=5 r=(3,1)
destroy #5
destroy #4
destroy #3
destroy #2
destroy #1Shelves and unique_ptr
Model two bookshelves that pass books between them, with no special member functions anywhere — the rule of zero. struct Book { std::string title; int pages; } with a two-argument constructor; class Shelf holds a name and a std::vector<std::unique_ptr<Book>>, and offers:
void add(std::string title, int pages)— appends a book made withstd::make_unique;std::unique_ptr<Book> take(std::size_t index)— removes and returns the book atindex(0-based), or a null pointer when there is no such book;void give(std::unique_ptr<Book> book)— appends a book received by value (the caller muststd::moveit in);void print() const—<name>: books=<n> pages=<total>, then one line<title> (<pages>)per book in shelf order.
The shelves are left and right. Read k and then k command lines:
add <shelf> <pages> <title>— the title is the rest of the line and may contain spaces;move <from> <to> <index>— takes the book from one shelf and gives it to the other, printingmoved <title> to <to>, orno book at <index>whentakereturns null;print <shelf>.
Input: k, then k lines. Output: as below.
6
add left 412 Dune
add left 88 Emma
add right 300 The Hobbit
move left right 0
print left
print right
prints
moved Dune to right
left: books=1 pages=88
Emma (88)
right: books=2 pages=712
The Hobbit (300)
Dune (412)In this module: Copies, moves and the rule of five
- Copy semantics — what a copy is, and when the default one is wrong
- Destructors and the rule of three
- Move semantics — rvalue references, std::move and noexcept
- The rule of five and the rule of zero
- Value categories, temporaries and copy elision
- Checkpoint — Copies, moves and the rule of five (this lesson)
← Value categories, temporaries and copy elision · Inheritance basics — deriving, protected and the order of construction →