Practice Questions — Smart Pointers and Modern Memory Management
← Back to NotesTopic-Specific Questions
Question 1
Easy
What is the output?
auto p = make_unique<int>(42);
cout << *p;make_unique creates a unique_ptr. Dereference with * to get the value.
42Question 2
Easy
What is the output?
auto p = make_shared<int>(99);
cout << p.use_count();A freshly created shared_ptr has a use_count of 1.
1Question 3
Easy
What is the output?
auto p1 = make_shared<int>(10);
auto p2 = p1;
auto p3 = p1;
cout << p1.use_count();Each copy of shared_ptr increments the reference count.
3Question 4
Easy
What is the output?
unique_ptr<int> p1 = make_unique<int>(5);
unique_ptr<int> p2 = move(p1);
cout << (p1 == nullptr) << " " << *p2;After move, the source unique_ptr becomes nullptr.
1 5Question 5
Easy
What is the output?
auto p = make_unique<int>(7);
cout << (p ? "valid" : "null");
p.reset();
cout << " " << (p ? "valid" : "null");reset() releases the managed object and sets the pointer to nullptr.
valid nullQuestion 6
Medium
What is the output?
auto p1 = make_shared<int>(50);
{
auto p2 = p1;
cout << p1.use_count() << " ";
}
cout << p1.use_count() << " " << *p1;When p2 goes out of scope, use_count decreases.
2 1 50Question 7
Medium
What is the output?
auto sp = make_shared<int>(100);
weak_ptr<int> wp = sp;
cout << wp.use_count() << " " << wp.expired() << " ";
sp.reset();
cout << wp.use_count() << " " << wp.expired();weak_ptr does not affect use_count. expired() returns true when the object no longer exists.
1 0 0 1Question 8
Medium
What is the output?
auto sp = make_shared<int>(77);
weak_ptr<int> wp = sp;
auto locked = wp.lock();
cout << (locked ? to_string(*locked) : "null") << " ";
cout << sp.use_count();lock() returns a shared_ptr, incrementing use_count.
77 2Question 9
Medium
What is the output?
struct X {
int val;
X(int v) : val(v) { cout << "X(" << val << ") ";
}
~X() { cout << "~X(" << val << ") "; }
};
{
auto p = make_unique<X>(1);
auto q = make_unique<X>(2);
}
cout << "done";Destructors run in reverse order of construction when leaving scope.
X(1) X(2) ~X(2) ~X(1) doneQuestion 10
Hard
What is the output?
auto p = make_unique<int>(10);
int* raw = p.get();
cout << *raw << " ";
p.reset();
cout << (p == nullptr);get() returns the raw pointer without releasing ownership. After reset(), p is null.
10 1Question 11
Hard
What is the output?
auto p = make_shared<int>(55);
auto q = move(p);
cout << (p == nullptr) << " " << q.use_count() << " " << *q;Moving a shared_ptr transfers ownership without changing use_count.
1 1 55Question 12
Hard
What is the output?
class A {
public:
A() { cout << "A "; }
~A() { cout << "~A "; }
};
vector<unique_ptr<A>> v;
v.push_back(make_unique<A>());
v.push_back(make_unique<A>());
cout << "clear ";
v.clear();Clearing a vector of unique_ptrs destroys all managed objects.
A A clear ~A ~AQuestion 13
Easy
What is the output?
auto p = make_shared<int>(25);
auto q = p;
auto r = p;
r.reset();
cout << p.use_count() << " " << *p;reset() releases one owner. Others remain.
2 25Question 14
Medium
What is the output?
auto p = make_unique<int>(100);
auto q = make_unique<int>(200);
p.swap(q);
cout << *p << " " << *q;swap exchanges the managed pointers between two unique_ptrs.
200 100Question 15
Medium
What is the output?
weak_ptr<int> wp;
{
auto sp = make_shared<int>(42);
wp = sp;
cout << wp.expired() << " ";
}
cout << wp.expired();When the last shared_ptr is destroyed, the weak_ptr expires.
0 1Question 16
Hard
What is the output?
struct Obj {
int val;
Obj(int v) : val(v) { cout << "C" << v << " "; }
~Obj() { cout << "D" << val << " "; }
};
auto p = make_unique<Obj>(1);
p = make_unique<Obj>(2);
cout << "end ";Assigning a new unique_ptr destroys the old managed object first.
C1 C2 D1 end D2Question 17
Easy
What is the output?
auto p = make_shared<int>(10);
auto q = make_shared<int>(20);
cout << (p == q) << " ";
q = p;
cout << (p == q) << " " << *q;Smart pointer comparison checks if they point to the same object.
0 1 10Question 18
Hard
What is the output?
auto p = make_unique<int>(50);
int* raw = p.release();
cout << (p == nullptr) << " " << *raw;
delete raw; // Must manually delete after release!release() transfers ownership to the raw pointer and sets unique_ptr to null.
1 50Mixed & Application Questions
Question 1
Easy
What is the output?
auto p = make_unique<string>("Hello");
cout << p->size() << " " << (*p)[0];unique_ptr uses -> to access the managed object's members.
5 HQuestion 2
Easy
What is the output?
shared_ptr<int> p1 = make_shared<int>(10);
shared_ptr<int> p2 = p1;
*p2 = 20;
cout << *p1;p1 and p2 point to the same int.
20Question 3
Medium
What is the output?
auto p1 = make_unique<int>(100);
auto p2 = make_unique<int>(200);
swap(p1, p2);
cout << *p1 << " " << *p2;swap exchanges the managed pointers.
200 100Question 4
Medium
What is the output?
unique_ptr<int[]> arr = make_unique<int[]>(4);
for (int i = 0; i < 4; i++) arr[i] = (i + 1) * 10;
cout << arr[0] << " " << arr[3];unique_ptr supports array subscript operator [].
10 40Question 5
Medium
What is the output?
auto sp = make_shared<int>(30);
weak_ptr<int> wp = sp;
sp = make_shared<int>(60);
auto locked = wp.lock();
cout << (locked ? "alive" : "dead");After sp is reassigned, what happens to the original int(30)?
deadQuestion 6
Hard
What is the output?
struct Node {
int val;
unique_ptr<Node> next;
Node(int v) : val(v), next(nullptr) {}
};
auto head = make_unique<Node>(1);
head->next = make_unique<Node>(2);
head->next->next = make_unique<Node>(3);
auto curr = head.get();
while (curr) {
cout << curr->val << " ";
curr = curr->next.get();
}get() returns a raw non-owning pointer for traversal.
1 2 3Question 7
Hard
What is the output?
class B {
public:
B() { cout << "B "; }
B(const B&) { cout << "Bc "; }
B(B&&) noexcept { cout << "Bm "; }
~B() { cout << "~B "; }
};
B b1;
B b2 = b1;
B b3 = move(b1);Copy constructor is called for b2, move constructor for b3.
B Bc Bm ~B ~B ~BQuestion 8
Medium
What is RAII and why is it fundamental to modern C++?
Think about tying resource lifetime to object lifetime.
RAII (Resource Acquisition Is Initialization) means that resources (memory, files, locks, sockets) are acquired in a constructor and released in a destructor. Since C++ guarantees destructor execution when objects go out of scope (including during stack unwinding from exceptions), RAII ensures resources are always properly cleaned up without explicit cleanup code. Smart pointers are the most common example of RAII.
Question 9
Hard
When should you use unique_ptr vs shared_ptr vs weak_ptr?
Think about ownership semantics.
Use
unique_ptr when there is a single clear owner of the resource (the most common case). Use shared_ptr when multiple parts of the code need to share ownership and the resource should only be deleted when all owners are done. Use weak_ptr to observe a shared resource without preventing its deletion, or to break circular references between shared_ptrs.Question 10
Hard
What is the difference between the Rule of Three, Rule of Five, and Rule of Zero?
Think about which special member functions you need to define.
Rule of Three (C++03): if you define any of destructor, copy constructor, or copy assignment, define all three. Rule of Five (C++11): adds move constructor and move assignment to the three. Rule of Zero: prefer using types that manage their own resources (smart pointers, std::string, std::vector) so you do not need to define any of the five. Modern C++ strongly favors the Rule of Zero.
Question 11
Medium
What is the output?
auto p = make_shared<vector<int>>(vector<int>{10, 20, 30});
cout << p->size() << " " << (*p)[1];shared_ptr to a vector. Use -> for member access.
3 20Question 12
Hard
What is the output?
auto sp = make_shared<int>(10);
weak_ptr<int> wp = sp;
auto sp2 = wp.lock();
cout << sp.use_count() << " ";
sp.reset();
sp2.reset();
cout << wp.expired();lock() creates a new shared_ptr. Both sp and sp2 must be reset for the object to be deleted.
2 1Question 13
Easy
What is the output?
unique_ptr<int> p;
cout << (p == nullptr) << " ";
p = make_unique<int>(99);
cout << (p == nullptr) << " " << *p;A default-constructed unique_ptr is nullptr.
1 0 99Question 14
Medium
What is the output?
struct Widget {
int id;
Widget(int i) : id(i) {}
};
auto w = make_unique<Widget>(42);
cout << w->id << " ";
w->id = 100;
cout << w->id;unique_ptr provides -> for accessing members of the managed object.
42 100Multiple Choice Questions
MCQ 1
Which header provides unique_ptr, shared_ptr, and weak_ptr?
Answer: B
B is correct. All smart pointers are defined in the
B is correct. All smart pointers are defined in the
<memory> header.MCQ 2
What does RAII stand for?
Answer: B
B is correct. RAII ties resource lifetime to object lifetime. Resources are acquired in constructors and released in destructors.
B is correct. RAII ties resource lifetime to object lifetime. Resources are acquired in constructors and released in destructors.
MCQ 3
Can you copy a unique_ptr?
Answer: B
B is correct. unique_ptr has a deleted copy constructor. It can only be moved, which transfers exclusive ownership to the destination.
B is correct. unique_ptr has a deleted copy constructor. It can only be moved, which transfers exclusive ownership to the destination.
MCQ 4
What does use_count() return for a shared_ptr?
Answer: B
B is correct.
B is correct.
use_count() returns the number of shared_ptr objects that share ownership of the managed resource.MCQ 5
What is the preferred way to create a unique_ptr?
Answer: B
B is correct.
B is correct.
make_unique is exception-safe and clearly expresses intent. Never use raw new with smart pointers.MCQ 6
What does weak_ptr::lock() return?
Answer: C
C is correct.
C is correct.
lock() returns a shared_ptr that shares ownership with the existing shared_ptrs. If the managed object has been deleted, it returns an empty shared_ptr.MCQ 7
What is the primary purpose of weak_ptr?
Answer: C
C is correct.
C is correct.
weak_ptr does not increment the reference count, breaking cycles that would otherwise prevent shared_ptrs from ever reaching use_count 0.MCQ 8
What happens to a unique_ptr after std::move?
Answer: B
B is correct. After
B is correct. After
std::move, the source unique_ptr becomes nullptr. Ownership is transferred to the destination.MCQ 9
What is the advantage of make_shared over using new with shared_ptr?
Answer: B
B is correct.
B is correct.
make_shared allocates the control block (reference counts) and the object in a single heap allocation, improving cache locality and performance.MCQ 10
What does p.get() return for a smart pointer?
Answer: B
B is correct.
B is correct.
get() returns the raw pointer managed by the smart pointer. The smart pointer retains ownership. Use this for interoperability with APIs that require raw pointers.MCQ 11
What is an rvalue reference (&&) used for?
Answer: C
C is correct. Rvalue references (
C is correct. Rvalue references (
&&) bind to temporaries and moved-from objects, enabling move constructors and move assignment operators to steal resources instead of copying.MCQ 12
What does the Rule of Zero state?
Answer: C
C is correct. The Rule of Zero says: if your class uses smart pointers and other RAII types for all resources, the compiler-generated defaults are correct and you need zero special member functions.
C is correct. The Rule of Zero says: if your class uses smart pointers and other RAII types for all resources, the compiler-generated defaults are correct and you need zero special member functions.
MCQ 13
What happens if you create two shared_ptrs from the same raw pointer?
Answer: B
B is correct. Each shared_ptr creates its own control block, both thinking they are the sole owner. When both go out of scope, they both try to delete the same pointer, causing undefined behavior (double free).
B is correct. Each shared_ptr creates its own control block, both thinking they are the sole owner. When both go out of scope, they both try to delete the same pointer, causing undefined behavior (double free).
MCQ 14
What does std::move actually do?
Answer: C
C is correct.
C is correct.
std::move is just a cast to T&&. It does not move anything by itself. The actual moving happens in the move constructor or move assignment operator that gets selected because of the rvalue reference.MCQ 15
What does p.reset() do on a unique_ptr?
Answer: B
B is correct.
B is correct.
reset() deletes the currently managed object and sets the smart pointer to nullptr. You can also pass a new raw pointer to reset(new_ptr) to manage a different object.MCQ 16
Why should you mark move constructors and move assignment operators as noexcept?
Answer: B
B is correct. Containers like
B is correct. Containers like
vector use move during reallocation only if the move constructor is noexcept. If it might throw, the container falls back to copying to maintain the strong exception guarantee.Coding Challenges
Challenge 1: Build a Unique Pointer Linked List
EasyCreate a singly linked list using unique_ptr where each node owns the next node. Insert 5 values and traverse the list printing each value.
Sample Input
Values: 10, 20, 30, 40, 50
Sample Output
10 -> 20 -> 30 -> 40 -> 50 -> null
Use unique_ptr<Node> for the next pointer. No raw new/delete.
#include <iostream>
#include <memory>
using namespace std;
struct Node {
int val;
unique_ptr<Node> next;
Node(int v) : val(v), next(nullptr) {}
};
int main() {
auto head = make_unique<Node>(10);
auto curr = head.get();
for (int v : {20, 30, 40, 50}) {
curr->next = make_unique<Node>(v);
curr = curr->next.get();
}
curr = head.get();
while (curr) {
cout << curr->val << " -> ";
curr = curr->next.get();
}
cout << "null" << endl;
return 0;
}Challenge 2: Shared Resource Counter
MediumCreate a shared_ptr to an integer. Pass it to 3 different functions that each store a copy. Print use_count after each step and verify it returns to 1 after all functions complete.
Sample Input
Initial value: 42
Sample Output
Created: count=1
After share1: count=2
After share2: count=3
After share3: count=4
All released: count=1
Value: 42
Use shared_ptr. Demonstrate reference counting.
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
int main() {
auto p = make_shared<int>(42);
cout << "Created: count=" << p.use_count() << endl;
vector<shared_ptr<int>> holders;
for (int i = 1; i <= 3; i++) {
holders.push_back(p);
cout << "After share" << i << ": count=" << p.use_count() << endl;
}
holders.clear();
cout << "All released: count=" << p.use_count() << endl;
cout << "Value: " << *p << endl;
return 0;
}Challenge 3: Factory Function Returning unique_ptr
MediumCreate a factory function that returns different shapes (Circle, Rectangle) as unique_ptr. Use polymorphism to call area() on each shape.
Sample Input
Circle(5.0), Rectangle(4.0, 6.0)
Sample Output
Circle area: 78.5398
Rectangle area: 24
Use unique_ptr for ownership. Virtual functions for polymorphism.
#include <iostream>
#include <memory>
#include <cmath>
using namespace std;
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double r;
public:
Circle(double radius) : r(radius) {}
double area() const override { return M_PI * r * r; }
};
class Rectangle : public Shape {
double w, h;
public:
Rectangle(double width, double height) : w(width), h(height) {}
double area() const override { return w * h; }
};
unique_ptr<Shape> createShape(const string& type, double a, double b = 0) {
if (type == "circle") return make_unique<Circle>(a);
if (type == "rectangle") return make_unique<Rectangle>(a, b);
return nullptr;
}
int main() {
auto c = createShape("circle", 5.0);
auto r = createShape("rectangle", 4.0, 6.0);
cout << "Circle area: " << c->area() << endl;
cout << "Rectangle area: " << r->area() << endl;
return 0;
}Challenge 4: Break a Circular Reference with weak_ptr
HardCreate a Person class where each person can have a best friend. First demonstrate the memory leak with shared_ptr (circular reference), then fix it using weak_ptr.
Sample Input
Arjun's best friend is Priya, Priya's best friend is Arjun
Sample Output
Arjun created
Priya created
Arjun's friend: Priya
Priya destroyed
Arjun destroyed
Use weak_ptr for the bestFriend pointer. Demonstrate lock() for safe access.
#include <iostream>
#include <memory>
using namespace std;
class Person {
public:
string name;
weak_ptr<Person> bestFriend; // weak_ptr breaks the cycle
Person(string n) : name(n) { cout << name << " created" << endl; }
~Person() { cout << name << " destroyed" << endl; }
void showFriend() {
if (auto f = bestFriend.lock())
cout << name << "'s friend: " << f->name << endl;
else
cout << name << " has no friend" << endl;
}
};
int main() {
auto arjun = make_shared<Person>("Arjun");
auto priya = make_shared<Person>("Priya");
arjun->bestFriend = priya;
priya->bestFriend = arjun;
arjun->showFriend();
return 0;
}Challenge 5: Implement a Simple Resource Pool with shared_ptr
HardCreate a simple connection pool that hands out shared_ptr objects. Track how many connections are active using use_count. When all shared_ptrs to a connection are released, the connection returns to the pool.
Sample Input
Get 2 connections, release 1, check active count
Sample Output
Connection 1 created
Connection 2 created
Active connections: 2
Releasing connection 2
Active connections: 1
Use shared_ptr and weak_ptr. No raw pointers.
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Connection {
int id;
public:
Connection(int i) : id(i) { cout << "Connection " << id << " created" << endl; }
~Connection() { cout << "Connection " << id << " destroyed" << endl; }
int getId() const { return id; }
};
class Pool {
vector<weak_ptr<Connection>> connections;
int nextId = 1;
public:
shared_ptr<Connection> getConnection() {
auto conn = make_shared<Connection>(nextId++);
connections.push_back(conn);
return conn;
}
int activeCount() {
int count = 0;
for (auto& wp : connections)
if (!wp.expired()) count++;
return count;
}
};
int main() {
Pool pool;
auto c1 = pool.getConnection();
{
auto c2 = pool.getConnection();
cout << "Active connections: " << pool.activeCount() << endl;
cout << "Releasing connection 2" << endl;
}
cout << "Active connections: " << pool.activeCount() << endl;
return 0;
}Need to Review the Concepts?
Go back to the detailed notes for this chapter.
Read Chapter NotesWant to learn C++ with a live mentor?
Explore our C++ Masterclass