当前位置: 首页 > ds >正文

【读书笔记】《Effective Modern C++》第4章 Smart Pointers

《Effective Modern C++》第4章 Smart Pointers

一、使用 std::unique_ptr 管理独占所有权(Item 18)

  1. 基本概念
    std::unique_ptr 提供对动态分配资源的独占所有权管理。与裸指针不同,unique_ptr 在离开作用域或被重置时自动释放所管理的资源。它禁止拷贝,只能移动。

  2. 使用方式
    推荐工厂函数方式创建:

    auto up = std::make_unique<Foo>(arg1, arg2);
    

    当需要转移所有权时:

    std::unique_ptr<Foo> up2 = std::move(up);
    // 此时 up 为空,up2 拥有资源
    
  3. 自定义删除器
    对于非 new 分配的资源,如 FILE* 或自定义句柄,可传入删除器类型:

    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("data.txt", "r"), &fclose);
    

    删除器在 unique_ptr 析构时被调用,保证资源释放。

  4. 注意事项

    • 禁止拷贝可防止重复释放;
    • 作为成员变量时,可用 = default 或手动定义移动构造/赋值;
    • 不要将裸指针与 unique_ptr 混合管理同一资源。

二、使用 std::shared_ptr 管理共享所有权(Item 19)

  1. 基本概念
    std::shared_ptr 通过内部控制块维护引用计数,允许多方共享同一对象。最后一个 shared_ptr 析构时,自动释放资源。

  2. 使用方式
    推荐工厂函数:

    auto sp1 = std::make_shared<Foo>(arg);
    auto sp2 = sp1;   // 引用计数增至 2
    sp1.reset();      // 计数减至 1
    // 最后 sp2 离开作用域时释放 Foo
    
  3. 性能与成本

    • 控制块与对象默认一次分配;
    • 引用计数操作是原子级别开销;
    • 拷贝和析构时都要修改计数。
  4. 循环引用问题
    当对象 A 和 B 相互持有 shared_ptr,会导致引用计数永不归零。必须通过 weak_ptr 打破循环。

三、使用 std::weak_ptr 管理可能失效的观察型指针(Item 20)

  1. 基本概念
    std::weak_ptr 是对 shared_ptr 管理资源的非拥有型引用,不增加引用计数。它记录 control block 的弱引用,可检测资源是否已释放。

  2. 使用方式

    std::shared_ptr<Foo> sp = std::make_shared<Foo>();
    std::weak_ptr<Foo> wp = sp;  // 计数不变if (auto guard = wp.lock()) {// guard 为 shared_ptr,安全访问guard->method();
    } else {// 对象已销毁
    }
    
  3. 应用场景

    • 观察者模式、缓存实现、回调注册时,不希望延长对象生命周期;
    • 打破 shared_ptr 循环引用的一端。
  4. 注意事项

    • 使用 lock() 获取 shared_ptr 并检查是否为空;
    • 避免直接将 weak_ptr 转为裸指针;
    • expired() 可用于简单过期检测,但 lock() 更安全。

四、优先使用 std::make_unique 和 std::make_shared(Item 21)

  1. 问题及原因
    直接 new 后再构造智能指针存在两次内存分配和潜在异常安全漏洞:一旦在 new 后抛出,容易泄漏。

  2. make 工厂函数优势

    • 单次内存分配:make_shared 在一次分配中创建控制块和对象;
    • 异常安全:资源在构造过程中即被智能指针管理,不会泄漏。
  3. 示例对比

    // 不推荐
    std::unique_ptr<Foo> up(new Foo(args));
    std::shared_ptr<Foo> sp(new Foo(args));// 推荐
    auto up2 = std::make_unique<Foo>(args);
    auto sp2 = std::make_shared<Foo>(args);
    
  4. 特殊情况

    • 需要自定义删除器时,仍需手动 new 并传入删除器;
    • make_shared 无法分离控制块内存,若希望控制释放时机可考虑手动创建。

五、Pimpl 习惯用法中在实现文件定义特殊成员函数(Item 22)

  1. Pimpl 模式目的
    隐藏实现细节、减少头文件依赖、加快编译速度。头文件中声明不完整类型,实际数据保存在实现类 Impl 中。

  2. 头文件示例(Widget.h)

    class Widget {
    public:Widget();~Widget();Widget(const Widget&);Widget& operator=(const Widget&);
    private:struct Impl;std::unique_ptr<Impl> pImpl;
    };
    
  3. 实现文件示例(Widget.cpp)

    struct Widget::Impl {// 数据成员
    };Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
    Widget::~Widget() = default;
    Widget::Widget(const Widget& w): pImpl(std::make_unique<Impl>(*w.pImpl)) {}
    Widget& Widget::operator=(const Widget& w) {*pImpl = *w.pImpl;return *this;
    }
    
  4. 原因与好处

    • Impl 定义对外部不可见,确保封装性;
    • 修改 Impl 后只需重新编译 cpp,减少依赖传播;
    • 特殊成员函数在 cpp 中定义时,Impl 完整可见,避免不完整类型使用错误。

总结

本章围绕智能指针的五个核心要点展开,从独占所有权到共享所有权,再到观察型 weak_ptr,以及工厂函数与 Pimpl 相结合的模式实践,全面阐明了现代 C++ 中资源管理的最佳实践。通过合理选用 unique_ptr、shared_ptr 和 weak_ptr,并配合 make_xxx 工厂函数和 Pimpl 隐藏实现细节,能够编写安全、高效且易于维护的 C++ 应用。

http://www.xdnf.cn/news/15499.html

相关文章:

  • 串口学习和蓝牙通信HC05(第八天)
  • es里的node和shard是一一对应的关系吗,可以多个shard分配到一个node上吗
  • Pandas-数据清洗与处理
  • 构建可落地的企业AI Agent,背后隐藏着怎样的技术密码?
  • redis汇总笔记
  • 什么时候需要用到 multiprocessing?
  • 基于 CentOS 7 的 LVS+DR+Web+NFS 旅游攻略分享平台部署
  • 【RA-Eco-RA6E2-64PIN-V1.0 开发板】ADC 电压的 LabVIEW 数据采集
  • 【读书笔记】《Effective Modern C++》第六章 Lambda Expressions
  • Windows 常用命令
  • vue防内存泄漏和性能优化浅解
  • 如何自动化处理TXT日志,提升工作效率新方式
  • RabbitMQ队列的选择
  • 03.Python 字符串中的空白字符处理
  • Springboot实现一个接口加密
  • 华为交换机 undo negotiation auto功能(华为交换机端口接光纤两端起不来)
  • 【Complete Search】-基础完全搜索-Basic Complete Search
  • JAVA 项目工程化实践
  • fatal: active `post-checkout` hook found during `git clone`
  • v-for中key值的作用:为什么我总被要求加这个‘没用的‘属性?
  • 大小为 K 且平均值大于等于阈值的子数组数目
  • “找到一个或多个多重定义的符号“(LNK2005 或 LNK1169)
  • 006_测试评估与安全实践
  • 深入理解 LangChain:AI 应用开发的全新范式
  • 面试150 填充每个节点的下一个右侧节点指针Ⅱ
  • 第一个Flink 程序 WordCount,词频统计(批处理)
  • ReAct论文解读(1)—什么是ReAct?
  • AI大模型计数能力的深度剖析:从理论缺陷到技术改进
  • Java行为型模式---观察者模式
  • macOS - Chrome 关闭自动更新