Katkat

Implement a Thread-Safe Reference-Counted Smart Pointer in C++

NVIDIASoftware Engineer · Onsite

Last updated

Implement a reference-counted smart pointer class template in C++ from scratch, with copy and move construction and assignment, reset, dereferencing and a use count. It tests ownership rules, self-assignment and exception safety, atomic reference counting with correct memory ordering, and extensions such as weak pointers, base-class conversion and reference cycles.

Implement a reference-counted smart pointer class template in C++ from scratch, similar in spirit to std::shared_ptr. Several owners may hold the same object; the object must be destroyed exactly once, when the last owner releases it, and never while any owner still uses it. This came up as a "system coding" exercise on an autonomous-driving team that works entirely in C++, so expect questions about resource ownership, copy and move semantics, and thread safety rather than about algorithms.

Provide at least this interface:

template <typename T>
class SharedPtr {
public:
 SharedPtr() noexcept; // empty, owns nothing
 explicit SharedPtr(T* p); // takes ownership of p (allocated with new)
 SharedPtr(const SharedPtr& other); // shares ownership
 SharedPtr(SharedPtr&& other) noexcept; // transfers ownership, leaves other empty
 SharedPtr& operator=(const SharedPtr& other);
 SharedPtr& operator=(SharedPtr&& other) noexcept;
 ~SharedPtr();

 void reset(T* p = nullptr); // release current object, optionally own p
 T* get() const noexcept;
 T& operator*() const;
 T* operator->() const noexcept;
 explicit operator bool() const noexcept;
 std::size_t use_count() const noexcept; // number of owners; 0 when empty
};

Constraints

  • C++17 or later. Do not use std::shared_ptr, std::unique_ptr or std::weak_ptr.
  • The pointer owns single objects allocated with new, not arrays.
  • An empty SharedPtr, including one constructed from a null pointer, reports use_count() == 0.

Clarifying Questions

  • Will copies of the same SharedPtr be created and destroyed on different threads at the same time, so that count updates must be thread-safe?
  • Must a SharedPtr to a derived class convert to a SharedPtr to its base class?
  • Are custom deleters or array support required?
  • Is a separate heap allocation for the count acceptable, or should the count live inside the managed object?
  • Is a non-owning "weak" reference needed?

Hints

Hint 1

Where the count lives: Decide where the count must be stored so that every copy sees the same value, and who is responsible for freeing that storage.

Hint 2

Trace an assignment: Walk through `a = b` when `a` and `b` own different objects, and again when they are the same object, counting every increment, decrement and deletion.

Follow-up questions

  • Add a weak pointer. What must the shared bookkeeping hold now, and when is it freed?
  • A `SharedPtr` to a base class is created from a pointer to a derived object, and the base class has no virtual destructor. What happens at the last release, and how can a smart pointer make this safe anyway?
  • How would a factory function put the object and its count in a single allocation, and what does that cost once weak pointers exist?
  • Two objects hold `SharedPtr`s to each other. What happens when all outside owners go away, and how do you fix it?