2026/4/18 12:27:00
网站建设
项目流程
宜兴建设局 审图中心 网站,WordPress做老用户管理,买了一个域名如何做网站,app展示网站好的#xff0c;这是一个关于 C 智能指针的全面指南#xff1a;C 智能指针完全指南#xff1a;原理、用法与避坑实战1. 核心原理#xff1a;RAII (资源获取即初始化)核心思想#xff1a; 将资源的生命周期与对象的生命周期绑定。资源#xff08;如动态分配的内存#xf…好的这是一个关于 C 智能指针的全面指南C 智能指针完全指南原理、用法与避坑实战1. 核心原理RAII (资源获取即初始化)核心思想将资源的生命周期与对象的生命周期绑定。资源如动态分配的内存在对象构造时获取在对象析构时自动释放。目的解决手动管理资源如new/delete易导致的内存泄漏、重复释放等问题。智能指针角色智能指针是 RAII 原则在动态内存管理上的具体实现。它们封装了原始指针并负责在其自身析构时自动释放所指向的内存。2. C 标准库智能指针标准库在memory头文件中提供了三种主要的智能指针2.1std::unique_ptr(独占所有权)核心特性独占所有权同一时刻只有一个unique_ptr可以指向一个给定的对象。它不能被复制。移动语义可以通过std::move进行所有权转移。轻量高效开销很小几乎等同于原始指针。适用场景明确对象有唯一所有者的情况。例如工厂函数返回对象、作为类成员拥有其他对象。基本用法#include memory #include iostream class MyClass { public: MyClass() { std::cout MyClass constructed\n; } ~MyClass() { std::cout MyClass destroyed\n; } void doSomething() { std::cout Doing something\n; } }; int main() { // 创建并拥有一个 MyClass 对象 std::unique_ptrMyClass ptr1(new MyClass()); ptr1-doSomething(); // 所有权转移 (ptr1 变为 nullptr) std::unique_ptrMyClass ptr2 std::move(ptr1); if (!ptr1) { std::cout ptr1 is now empty\n; } ptr2-doSomething(); // 使用 make_unique (C14 起推荐) auto ptr3 std::make_uniqueMyClass(); ptr3-doSomething(); // ptr3 离开作用域自动销毁其管理的对象 return 0; }自定义删除器可以指定释放资源的方式如关闭文件句柄、释放特定类型内存。auto FileDeleter [](FILE* fp) { if (fp) fclose(fp); }; std::unique_ptrFILE, decltype(FileDeleter) filePtr(fopen(data.txt, r), FileDeleter);2.2std::shared_ptr(共享所有权)核心特性共享所有权多个shared_ptr可以指向同一个对象。引用计数内部维护一个引用计数器。当一个新的shared_ptr指向该对象时计数器增加当shared_ptr析构时计数器减少。计数器归零时自动删除对象。适用场景需要多个部分共享访问同一对象且无法确定哪个部分最后使用的情况。基本用法#include memory #include iostream class MyResource { public: MyResource() { std::cout Resource created\n; } ~MyResource() { std::cout Resource destroyed\n; } }; int main() { // 创建共享对象 (推荐使用 make_shared) std::shared_ptrMyResource sharedPtr1 std::make_sharedMyResource(); { // 共享所有权 (引用计数 1) std::shared_ptrMyResource sharedPtr2 sharedPtr1; std::cout Use count inside inner scope: sharedPtr2.use_count() \n; // 输出 2 } // sharedPtr2 析构引用计数 -1 std::cout Use count outside inner scope: sharedPtr1.use_count() \n; // 输出 1 // sharedPtr1 离开作用域引用计数归零对象销毁 return 0; }注意点控制块开销shared_ptr需要额外的内存存储引用计数和控制信息。make_shared通常能优化此开销。线程安全引用计数的增减是原子操作但指向的对象本身是否线程安全取决于其自身设计。避免混用原始指针不要用同一个原始指针初始化多个独立的shared_ptr这会导致多个控制块和双重释放。2.3std::weak_ptr(弱引用)核心特性不拥有所有权weak_ptr指向一个由shared_ptr管理的对象但不增加其引用计数。观察者用于观察对象是否存在而不会阻止其销毁。解决循环引用主要用途之一。基本用法必须通过shared_ptr创建或赋值。要访问对象需先尝试将其提升 (lock) 为shared_ptr。#include memory #include iostream class MyResource; class Observer { public: void observe(std::shared_ptrMyResource res) { weakResource res; // 用 shared_ptr 初始化 weak_ptr } void tryAccess() { auto sharedRes weakResource.lock(); // 尝试提升为 shared_ptr if (sharedRes) { std::cout Resource is still alive, accessing it.\n; } else { std::cout Resource has been destroyed.\n; } } private: std::weak_ptrMyResource weakResource; }; class MyResource { public: MyResource() { std::cout Resource created\n; } ~MyResource() { std::cout Resource destroyed\n; } }; int main() { auto resource std::make_sharedMyResource(); Observer obs; obs.observe(resource); obs.tryAccess(); // 输出 Resource is still alive... resource.reset(); // 销毁 resource obs.tryAccess(); // 输出 Resource has been destroyed. return 0; }3. 实战避坑循环引用问题描述当两个或多个对象通过shared_ptr相互持有对方导致它们的引用计数永远无法归零从而发生内存泄漏。示例场景class Node { public: std::shared_ptrNode next; // 指向下一个节点 std::shared_ptrNode prev; // 指向上一个节点 (导致循环引用) Node() { std::cout Node created\n; } ~Node() { std::cout Node destroyed\n; } }; int main() { auto node1 std::make_sharedNode(); auto node2 std::make_sharedNode(); node1-next node2; // node1 持有 node2 node2-prev node1; // node2 持有 node1 (循环引用!) // node1 和 node2 离开作用域但它们的引用计数都是 1 (彼此持有)对象不会被销毁 return 0; // 输出: Node created\nNode created\n (没有销毁信息) }解决方案使用weak_ptr打破循环class Node { public: std::shared_ptrNode next; std::weak_ptrNode prev; // 将其中一个指针改为 weak_ptr Node() { std::cout Node created\n; } ~Node() { std::cout Node destroyed\n; } }; int main() { auto node1 std::make_sharedNode(); auto node2 std::make_sharedNode(); node1-next node2; node2-prev node1; // node2 持有 node1 的弱引用 // 现在 node1 引用计数 1 (仅被 main 持有), node2 引用计数 2 (被 main 和 node1 持有) // 离开作用域: // main 释放 node1 - node1 引用计数归零销毁 node1 (释放 node1-next 会减少 node2 引用计数到 1) // main 释放 node2 - node2 引用计数归零销毁 node2 return 0; // 输出: Node created\nNode created\nNode destroyed\nNode destroyed }4. 总结与最佳实践优先使用智能指针避免手动new/delete。默认首选unique_ptr除非需要共享所有权。使用make_unique和make_shared更安全、更高效。谨慎使用shared_ptr注意控制块开销和潜在的循环引用。善用weak_ptr解决循环引用问题和作为观察者。避免混用原始指针和智能指针管理同一块内存。明确所有权设计时清晰定义对象的所有权归属。注意线程安全shared_ptr的引用计数是线程安全的但对象本身的操作可能需要额外的同步。遵循这些原则和实践可以显著提高 C 程序的内存安全性和可维护性。