What this topic covers
12 subtopics- Shallow vs deep copy7
- Copying objects6
- Move semantics6
- Garbage collection5
- Object lifetime5
- Smart pointers4
- Copy semantics3
- Deterministic cleanup3
- Destruction order2
- Reference counting2
- Destructors1
- RAII1
- Beginner 15
- Intermediate 15
- Advanced 15
About this topic
Every object is created, used and eventually destroyed. C++ destroys objects deterministically – at the end of their scope or on delete – which makes RAII (resources released in destructors) possible. Java and Python free memory automatically (tracing garbage collection, reference counting), so other resources are released with try-with-resources and with. Copying raises the classic question: does the copy share inner objects (shallow) or duplicate them (deep)? Every order printed here is the real one.
Free preview
Sample questions
Try each one first, then open the answer for the explanation and why the other options are wrong.
What does this print?
#include <iostream>
#include <string>
struct Tracer {
std::string n;
Tracer(std::string s) : n(s) { std::cout << "+" << n << ' '; }
~Tracer() { std::cout << "-" << n << ' '; }
};
int main() {
{
Tracer a("a");
Tracer b("b");
}
std::cout << "end\n";
}
Show answer and explanation
Explanation
Local objects are destroyed when their block ends, in the reverse order of construction – b depended on nothing created after it, so it goes first.
Why the other options are wrong
- ABoth live until the end of the block.
- BDestruction reverses construction.
- DThey die at the closing brace, before "end".
This class owns a raw pointer and relies on the default copy constructor. What goes wrong at run time?
#include <iostream>
struct Buffer {
int* data;
Buffer() : data(new int(42)) {}
~Buffer() { delete data; }
};
int main() {
Buffer a;
Buffer b = a;
std::cout << *b.data << '\n';
}
Show answer and explanation
Explanation
A memberwise copy of a pointer is a shallow copy: two owners, one allocation. When both destructors run, the memory is freed twice (AddressSanitizer stops the program). Follow the rule of three/five, or own the memory with std::unique_ptr / std::vector.
Why the other options are wrong
- AFreeing twice is undefined behaviour, not a leak.
- BThe default copy compiles.
- DIt points to the same int as a.
All five special members print. What does this print?
#include <iostream>
#include <utility>
struct T {
T() { std::cout << "ctor "; }
T(const T&) { std::cout << "copy-ctor "; }
T(T&&) noexcept { std::cout << "move-ctor "; }
T& operator=(const T&) { std::cout << "copy-assign "; return *this; }
T& operator=(T&&) noexcept { std::cout << "move-assign "; return *this; }
};
int main() {
T a;
T b = std::move(a);
T c;
c = std::move(b);
T d = c;
c = d;
std::cout << '\n';
}
Show answer and explanation
Explanation
Creating a new object from another uses a constructor; changing an existing object uses an assignment operator. An rvalue (std::move) selects the move version, a named lvalue the copy version.
Why the other options are wrong
- Astd::move enables the move overloads.
- Bb is being created, so it is a constructor.
- Dc already exists, so it is an assignment.