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
ソースの最終更新活動
2026年2月11日 17:47
検出された SKILL.md の言語
英語
スター
198
フォーク
21

インストール方法

デフォルトでは、最初にソースを確認する 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で見る