Skip to main content

cpp-smart-pointers

Use when C++ smart pointers including unique_ptr, shared_ptr, and weak_ptr for automatic memory management following RAII principles.

معلومات المصدر

المستودع
TheBushidoCollective/han
آخر نشاط في المصدر
١١ فبراير ٢٠٢٦ في ١٧:٤٧
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٩٨
التفرعات
٢١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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 <memory> #include <iostream> #include <fstream> // RAII wrapper for file handle class FileHandle { std::unique_ptr<std::FILE, decltype(&std::fclose)> 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<int>(42); // Automatic cleanup even if exception thrown } // RAII for multiple resources void multiple_resources() { auto file1 = std::make_unique<FileHandle>("data.txt", "r"); auto file2 = std::make_unique<FileHandle>("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 <memory> #include <vector> #include <iostream> 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<Widget> w1(new Widget(1)); auto w2 = std::make_unique<Widget>(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<Widget> w3 = std::move(w1); // w1 is now nullptr // Cannot copy // std::unique_ptr<Widget> w4 = w3; // Compiler error } // Factory function returning unique_ptr std::unique_ptr<Widget> create_widget(int id) { return std::make_unique<Widget>(id); } // Container of unique_ptr void container_example() { std::vector<std::unique_ptr<Widget>> widgets; widgets.push_back(std::make_unique<Widget>(1)); widgets.push_back(std::make_unique<Widget>(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<std::FILE, FileCloser> file( std::fopen("data.txt", "r") ); // Lambda deleter auto deleter = [](int* p) { std::cout << "Deleting: " << *p << "\n"; delete p; }; std::unique_ptr<int, decltype(deleter)> 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 <memory> #include <vector> #include <iostream> 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<Resource> r1(new Resource(1)); auto r2 = std::make_shared<Resource>(2); // Preferred - one allocation // Share ownership std::shared_ptr<Resource> r3 = r2; std::cout << "Use count: " << r2.use_count() << "\n"; // 2 // Multiple owners { std::shared_ptr<Resource> 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<Node> 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<Node>(1); head->next = std::make_shared<Node>(2); head->next->next = std::make_shared<Node>(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<Resource>(1); std::shared_ptr<Resource> 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>(); data->x = 10; data->y = 20; // Create shared_ptr to member, but keeps entire object alive std::shared_ptr<int> 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 <memory> #include <iostream> // Without weak_ptr: circular reference leak class BadParent; class BadChild { public: std::shared_ptr<BadParent> parent; ~BadChild() { std::cout << "Child destroyed\n"; } }; class BadParent { public: std::shared_ptr<BadChild> child; ~BadParent() { std::cout << "Parent destroyed\n"; } }; void circular_reference_leak() { auto parent = std::make_shared<BadParent>(); auto child = std::make_shared<BadChild>(); 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> parent; // weak_ptr breaks cycle ~Child() { std::cout << "Child destroyed\n"; } }; class Parent { public: std::shared_ptr<Child> child; ~Parent() { std::cout << "Parent destroyed\n"; } }; void weak_ptr_example() { auto parent = std::make_shared<Parent>(); auto child = std::make_shared<Child>(); parent->child = child; child->parent = parent; // No circular reference } // Both objects destroyed properly // Using weak_ptr void weak_ptr_usage() { std::weak_ptr<Resource> weak; { auto shared = std::make_shared<Resource>(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<std::weak_ptr<class Observer>> observers_; public: void attach(std::shared_ptr<Observer> 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 <memory> #include <iostream> #include <vector> class Task : public std::enable_shared_from_this<Task> { int id_; std::vector<std::shared_ptr<Task>> dependencies_; public: Task(int id) : id_(id) {} void add_dependency(std::shared_ptr<Task> dep) { dependencies_.push_back(dep); } // Register this task as dependency of another void register_with(std::shared_ptr<Task> 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<Task>(1); auto task2 = std::make_shared<Task>(2); task1->register_with(task2); // task2 now has a shared_ptr to task1 } // Callback registration
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub