Implement and Explain a C++ Smart Pointer
NVIDIASoftware Engineer · Technical ScreenLast updated
Tests understanding of C++ memory management, RAII, explicit ownership semantics, and the Rule of Five by implementing a reference-counted shared smart pointer.
Problem Statement
In systems programming and high-performance runtimes, robust resource management is critical to prevent memory leaks and dangling pointers. Your task is to implement a simplified, generic shared ownership smart pointer class template, SharedPtr, modeled after C++'s std::shared_ptr.
Your implementation should track references using a heap-allocated reference counter (control block) so that the managed object is destroyed and its memory freed exactly when the last owning SharedPtr is destroyed or reset.
Requirements
Implement the SharedPtr template with the following interface:
template
class SharedPtr {
public:
// Explicit constructor taking a raw pointer (defaults to nullptr)
explicit SharedPtr(T* ptr = nullptr);
// Destructor
~SharedPtr();
// Copy semantics (Rule of Five)
SharedPtr(const SharedPtr& other);
SharedPtr& operator=(const SharedPtr& other);
// Move semantics
SharedPtr(SharedPtr&& other) noexcept;
SharedPtr& operator=(SharedPtr&& other) noexcept;
// Observers & Dereference operators
T& operator*() const;
T* operator->() const;
T* get() const;
size_t use_count() const;
// Modifier
void reset(T* ptr = nullptr);
};
Example Usage
SharedPtr sp1(new int(42));
assert(sp1.use_count() == 1);
assert(*sp1 == 42);
{
SharedPtr sp2 = sp1;
assert(sp1.use_count() == 2);
assert(sp2.use_count() == 2);
*sp2 = 100;
}
// sp2 went out of scope
assert(sp1.use_count() == 1);
assert(*sp1 == 100);
sp1.reset();
assert(sp1.use_count() == 0);
assert(sp1.get() == nullptr);
Edge Cases to Consider
- Self-assignment: Handling cases like
sp = sp;without prematurely freeing memory. - Null pointers:
SharedPtr(nullptr)should initialize cleanly without allocating or incrementing counts unnecessarily, or must handle a zerouse_count()gracefully. - Reassignment /
reset: Appropriately decrementing existing counts and freeing existing resources before taking on new ones.
Hints
Hint 1
Maintain two raw pointer members: one to the managed object of type `T*` and one to a heap-allocated counter of type `size_t*`.
Hint 2
Use the copy-and-swap idiom for the copy assignment operator to ensure strong exception safety and automatically handle self-assignment.
Hint 3
Make sure that moving a `SharedPtr` nulls out the source pointer and reference counter so its destructor does not decrement the count.
Follow-up questions
- How would you ensure thread safety for concurrent copies and destructions (e.g., using `std::atomic`)?
- Why does `std::make_shared` perform a single heap allocation instead of two, and how would you redesign the control block to support that?
- How does `std::weak_ptr` resolve cyclic reference issues, and what changes would be needed in the control block to support weak references?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.