Files
uLib/docs/smart_pointer.md

4.7 KiB

SmartPointer Documentation

uLib::SmartPointer<T> is a shared ownership smart pointer implementation designed for the uLib ecosystem. While it shares many similarities with std::shared_ptr, it includes specific features for legacy compatibility, local reference wrapping, and integrated Boost serialization.

Table of Contents

  1. Overview
  2. Key Differences from std::shared_ptr
  3. Common Usage
  4. Construction and Assignment
  5. Wrapping References
  6. Polymorphism and Casting
  7. Serialization
  8. Thread Safety

Overview

A SmartPointer manages the lifetime of an object through reference counting. When the last SmartPointer owning an object is destroyed or reset, the object is automatically deleted (unless a custom deleter is provided).

The implementation uses an internal ControlBlock to manage the reference count and an optional deleter function.

Key Differences from std::shared_ptr

Feature uLib::SmartPointer<T> std::shared_ptr<T>
Default Constructor Initializes to nullptr. Initializes to nullptr.
Implicit Conversion Implicitly converts from T* and to T*. Explicit construction from T*, no implicit conversion to T*.
Reference Wrapping Direct support for wrapping T& with a no-op deleter. Requires explicit custom deleter [](T*){}.
Serialization Built-in Boost.Serialization support. Requires external serialization helpers.

Note

The default constructor SmartPointer<T>() now initializes to nullptr, matching standard C++ smart pointer behavior.

Common Usage

#include "Core/SmartPointer.h"

// 1. Allocation via default constructor (Allocates a new MyObject)
uLib::SmartPointer<MyObject> ptr; 

// 2. Explicit null pointer
uLib::SmartPointer<MyObject> null_ptr(nullptr);

// 3. From raw pointer
uLib::SmartPointer<MyObject> manual_ptr(new MyObject(args));

// 4. Accessing members
ptr->DoSomething();
(*ptr).Value = 10;

// 5. Checking validity
if (ptr) {
    // ...
}

Construction and Assignment

Allocation and Ownership

  • SmartPointer(): Initializes to nullptr (Standard behavior).
  • SmartPointer(nullptr): Initializes to null.
  • SmartPointer(T* ptr): Takes ownership of the raw pointer (implicit conversion allowed).
  • SmartPointer(T& ref): Wraps an existing reference with a no-op deleter (implicit conversion allowed).
  • SmartPointer(T* ptr, Deleter d): Takes ownership and uses a custom deleter.
  • SmartPointer(const SmartPointer* other): Creates a copy from a pointer to another SmartPointer.

Move and Copy

  • Supports standard copy and move semantics. Move operations transfer ownership without incrementing the reference count.

Pointer Access

  • get() / Get(): Returns the underlying raw pointer.
  • operator T*(): Implicit conversion to raw pointer (legacy support).

Wrapping References

The SmartPointer can wrap an existing object (e.g., on the stack) without taking ownership:

MyObject stackObj;
uLib::SmartPointer<MyObject> spt(stackObj); 
// spt will NOT delete stackObj when it goes out of scope.

Polymorphism and Casting

SmartPointer supports assignment between compatible types (base/derived). For explicit casting, use the following utilities:

  • uLib::static_pointer_cast<T>(ptr)
  • uLib::dynamic_pointer_cast<T>(ptr)
  • uLib::const_pointer_cast<T>(ptr)
  • uLib::reinterpret_pointer_cast<T>(ptr)

Example:

uLib::SmartPointer<Derived> derived(new Derived());
uLib::SmartPointer<Base> base = derived; // Automatic upcast

auto derived2 = uLib::dynamic_pointer_cast<Derived>(base); // Downcast

Serialization

SmartPointer is fully integrated with boost::serialization. It tracks ControlBlock identity during serialization to ensure that multiple shared pointers to the same object are correctly reconstructed as a single shared instance.

#include <boost/archive/text_oarchive.hpp>

void save(const uLib::SmartPointer<MyObject>& ptr, std::ostream& os) {
    boost::archive::text_oarchive oa(os);
    oa << ptr;
}

Thread Safety

  • The reference count is managed using std::atomic<uint32_t>, making the increment/decrement operations thread-safe.
  • Note: While the reference counter itself is thread-safe, the object pointed to by the SmartPointer is not automatically protected. Standard thread-safety rules for the underlying type T apply.
  • Multiple threads can read the same SmartPointer concurrently. Concurrent modification (assignment/reset) of the same SmartPointer instance by different threads requires external synchronization.