--- name: cpp-smart-pointers user-invocable: false description: Use when C++ smart pointers including unique_ptr, shared_ptr, and weak_ptr for automatic memory management following RAII principles. allowed-tools: - Read - Write - Edit - Grep - Glob - Bash --- # C++ Smart Pointers Smart pointers provide automatic memory management through RAII (Resource Acquisition Is Initialization), eliminating manual `new` and `delete` calls. They prevent memory leaks, dangling pointers, and double-free errors while expressing ownership semantics clearly. ## RAII Principles RAII ties resource lifetime to object lifetime, ensuring automatic cleanup when objects go out of scope. ```cpp #include #include #include // RAII wrapper for file handle class FileHandle { std::unique_ptr file_; public: FileHandle(const char* filename, const char* mode) : file_(std::fopen(filename, mode), &std::fclose) { if (!file_) { throw std::runtime_error("Failed to open file"); } } std::FILE* get() { return file_.get(); } // No need for explicit destructor - RAII handles cleanup }; // Traditional approach (error-prone) void manual_memory() { int* ptr = new int(42); // If exception thrown here, memory leaks! delete ptr; } // RAII approach (safe) void raii_memory() { auto ptr = std::make_unique(42); // Automatic cleanup even if exception thrown } // RAII for multiple resources void multiple_resources() { auto file1 = std::make_unique("data.txt", "r"); auto file2 = std::make_unique("output.txt", "w"); // Both files automatically closed in reverse order } ``` ## unique_ptr - Exclusive Ownership `unique_ptr` represents exclusive ownership with zero runtime overhead and move-only semantics. ```cpp #include #include #include class Widget { int id_; public: Widget(int id) : id_(id) { std::cout << "Widget " << id_ << " created\n"; } ~Widget() { std::cout << "Widget " << id_ << " destroyed\n"; } int id() const { return id_; } }; void unique_ptr_basics() { // Create unique_ptr std::unique_ptr w1(new Widget(1)); auto w2 = std::make_unique(2); // Preferred // Access members std::cout << "Widget ID: " << w2->id() << "\n"; // Release ownership Widget* raw = w2.release(); delete raw; // Now we're responsible // Reset to new object w1.reset(new Widget(3)); // Old widget destroyed // Get raw pointer (ownership retained) Widget* ptr = w1.get(); // Move ownership (unique_ptr is move-only) std::unique_ptr w3 = std::move(w1); // w1 is now nullptr // Cannot copy // std::unique_ptr w4 = w3; // Compiler error } // Factory function returning unique_ptr std::unique_ptr create_widget(int id) { return std::make_unique(id); } // Container of unique_ptr void container_example() { std::vector> widgets; widgets.push_back(std::make_unique(1)); widgets.push_back(std::make_unique(2)); // Move from container auto w = std::move(widgets[0]); // widgets[0] is now nullptr } // Custom deleter struct FileCloser { void operator()(std::FILE* fp) const { if (fp) { std::cout << "Closing file\n"; std::fclose(fp); } } }; void custom_deleter_example() { std::unique_ptr file( std::fopen("data.txt", "r") ); // Lambda deleter auto deleter = [](int* p) { std::cout << "Deleting: " << *p << "\n"; delete p; }; std::unique_ptr ptr(new int(42), deleter); } ``` ## shared_ptr - Shared Ownership `shared_ptr` enables shared ownership with reference counting, allowing multiple pointers to the same object. ```cpp #include #include #include class Resource { int id_; public: Resource(int id) : id_(id) { std::cout << "Resource " << id_ << " created\n"; } ~Resource() { std::cout << "Resource " << id_ << " destroyed\n"; } int id() const { return id_; } }; void shared_ptr_basics() { // Create shared_ptr std::shared_ptr r1(new Resource(1)); auto r2 = std::make_shared(2); // Preferred - one allocation // Share ownership std::shared_ptr r3 = r2; std::cout << "Use count: " << r2.use_count() << "\n"; // 2 // Multiple owners { std::shared_ptr r4 = r2; std::cout << "Use count: " << r2.use_count() << "\n"; // 3 } // r4 destroyed std::cout << "Use count: " << r2.use_count() << "\n"; // 2 // Check if valid if (r2) { std::cout << "r2 is valid\n"; } // Reset r2.reset(); // Decrements reference count std::cout << "Use count: " << r3.use_count() << "\n"; // 1 } // Shared ownership in data structures class Node { public: int value; std::shared_ptr next; Node(int v) : value(v), next(nullptr) { std::cout << "Node " << value << " created\n"; } ~Node() { std::cout << "Node " << value << " destroyed\n"; } }; void linked_list_example() { auto head = std::make_shared(1); head->next = std::make_shared(2); head->next->next = std::make_shared(3); // All nodes automatically destroyed when head goes out of scope } // Converting between shared_ptr and unique_ptr void pointer_conversion() { // unique_ptr to shared_ptr auto u = std::make_unique(1); std::shared_ptr s = std::move(u); // u is now nullptr // Cannot convert shared_ptr to unique_ptr (shared ownership) } // Aliasing constructor struct Data { int x, y; }; void aliasing_example() { auto data = std::make_shared(); data->x = 10; data->y = 20; // Create shared_ptr to member, but keeps entire object alive std::shared_ptr px(data, &data->x); std::cout << "Use count: " << data.use_count() << "\n"; // 2 } ``` ## weak_ptr - Breaking Cycles `weak_ptr` provides non-owning references to `shared_ptr` objects, preventing circular reference memory leaks. ```cpp #include #include // Without weak_ptr: circular reference leak class BadParent; class BadChild { public: std::shared_ptr parent; ~BadChild() { std::cout << "Child destroyed\n"; } }; class BadParent { public: std::shared_ptr child; ~BadParent() { std::cout << "Parent destroyed\n"; } }; void circular_reference_leak() { auto parent = std::make_shared(); auto child = std::make_shared(); parent->child = child; child->parent = parent; // Circular reference - memory leak! } // Neither object destroyed! // With weak_ptr: breaks the cycle class Parent; class Child { public: std::weak_ptr parent; // weak_ptr breaks cycle ~Child() { std::cout << "Child destroyed\n"; } }; class Parent { public: std::shared_ptr child; ~Parent() { std::cout << "Parent destroyed\n"; } }; void weak_ptr_example() { auto parent = std::make_shared(); auto child = std::make_shared(); parent->child = child; child->parent = parent; // No circular reference } // Both objects destroyed properly // Using weak_ptr void weak_ptr_usage() { std::weak_ptr weak; { auto shared = std::make_shared(1); weak = shared; std::cout << "Use count: " << shared.use_count() << "\n"; // 1 std::cout << "Weak count: " << weak.use_count() << "\n"; // 1 // Lock to access object if (auto locked = weak.lock()) { std::cout << "Resource still alive: " << locked->id() << "\n"; std::cout << "Use count: " << locked.use_count() << "\n"; // 2 } } // shared destroyed // Object is gone if (auto locked = weak.lock()) { std::cout << "Resource still alive\n"; } else { std::cout << "Resource destroyed\n"; } std::cout << "Expired: " << weak.expired() << "\n"; // true } // Observer pattern with weak_ptr class Observable { std::vector> observers_; public: void attach(std::shared_ptr observer) { observers_.push_back(observer); } void notify() { // Clean up expired observers observers_.erase( std::remove_if(observers_.begin(), observers_.end(), [](const auto& weak) { return weak.expired(); }), observers_.end() ); // Notify active observers for (auto& weak : observers_) { if (auto observer = weak.lock()) { // Notify observer } } } }; ``` ## enable_shared_from_this `enable_shared_from_this` allows objects to create `shared_ptr` instances pointing to themselves safely. ```cpp #include #include #include class Task : public std::enable_shared_from_this { int id_; std::vector> dependencies_; public: Task(int id) : id_(id) {} void add_dependency(std::shared_ptr dep) { dependencies_.push_back(dep); } // Register this task as dependency of another void register_with(std::shared_ptr other) { // Get shared_ptr to this other->add_dependency(shared_from_this()); } int id() const { return id_; } }; void shared_from_this_example() { auto task1 = std::make_shared(1); auto task2 = std::make_shared(2); task1->register_with(task2); // task2 now has a shared_ptr to task1 } // Callback registration class Button : public std::enable_shared_from_this