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

Effective C++ 条款17:以独立语句将newed对象置入智能指针

Effective C++ 条款17:以独立语句将newed对象置入智能指针


核心思想使用智能指针管理动态分配的对象时,必须确保new操作与智能指针构造在同一独立语句中完成,避免编译器优化顺序导致的内存泄漏。

⚠️ 1. 跨语句初始化的危险性

资源泄漏场景

// 危险:跨语句初始化智能指针
processWidget(std::shared_ptr<Widget>(new Widget), riskyFunction());

编译器可能的执行顺序

  1. 执行 new Widget(分配内存)
  2. 调用 riskyFunction()(可能抛出异常)
  3. 构造 shared_ptr(管理资源)

风险分析

  • riskyFunction() 抛出异常 → 步骤1分配的Widget对象内存泄漏
  • shared_ptr 尚未接管资源 → 无自动释放机制

🚨 2. 解决方案:独立语句初始化

安全初始化模式

// 正确:独立语句完成资源分配和智能指针构造
std::shared_ptr<Widget> pw = std::make_shared<Widget>(); // 推荐方式// 或显式new+智能指针构造(同一语句)
std::shared_ptr<Widget> pw(new Widget); // 安全构造processWidget(pw, riskyFunction()); // 异常安全调用

现代C++最佳实践

// 优先使用std::make_shared(C++11+)
auto pw = std::make_shared<Widget>(); // 需自定义删除器时:
auto pw = std::shared_ptr<Widget>(new Widget, customDeleter);

⚖️ 3. 关键原则与注意事项
原则说明违反后果
单语句构造原则new操作与智能指针构造必须在同一独立语句完成资源泄漏风险
优先make_shared/make_unique使用工厂函数保证异常安全(C++11/14)消除显式new
避免函数参数内构造禁止在函数调用参数列表内直接new+智能指针构造编译器重排风险
扩展至所有资源管理类规则同样适用于自定义RAII类通用资源安全原则

编译器优化陷阱

C++标准允许编译器重排函数参数求值顺序(未指定顺序)

// 编译器可能的重排顺序:
1. new Widget        // 分配资源
2. riskyFunction()   // 可能抛出异常
3. shared_ptr构造    // 未执行 → 资源泄漏

自定义RAII类的安全用法

// 自定义资源管理类
class DBConnection { /* ... */ };
class DBHandler {
public:explicit DBHandler(DBConnection* conn) : conn_(conn) {}~DBHandler() { conn_->close(); }
private:DBConnection* conn_;
};// 安全初始化:
DBConnection* dbc = new DBConnection;  // 危险:分离语句
DBHandler handler(dbc);                 // 可能泄漏// 正确:同一语句完成
DBHandler handler(new DBConnection);    // 异常安全

💡 关键原则总结

  1. 异常安全第一原则
    • 动态资源必须立即被资源管理对象接管
    • new操作与RAII对象构造必须原子化完成
  2. make函数优先原则
    • std::make_shared<>()(C++11)
    • std::make_unique<>()(C++14)
    • 避免显式new操作
  3. 禁用复杂参数表达式
    • 禁止在函数调用参数内组合new与智能指针构造
    • 禁用多步操作初始化智能指针

错误用法重现

// 危险:可能泄漏资源的写法
processResource(std::unique_ptr<Resource>(new Resource), // 风险点loadConfig() // 可能抛出异常
);

安全重构方案

// 方案1:独立语句构造(基础)
auto res = std::unique_ptr<Resource>(new Resource);
processResource(std::move(res), loadConfig());// 方案2:make_unique(推荐,C++14+)
auto res = std::make_unique<Resource>();
processResource(std::move(res), loadConfig());// 方案3:延迟调用(异常安全封装)
auto callProcess = [](auto&&... args) {auto res = std::make_unique<Resource>();processResource(std::move(res), std::forward<decltype(args)>(args)...);
};
callProcess(loadConfig());
http://www.lryc.cn/news/607834.html

相关文章:

  • 易华路副总经理兼交付管理中心部门经理于江平受邀PMO大会主持人
  • Elasticsearch+Logstash+Filebeat+Kibana单机部署
  • RabbitMQ面试精讲 Day 7:消息持久化与过期策略
  • 用Unity结合VCC更改人物模型出现的BUG
  • 个人笔记UDP
  • 内存、硬盘与缓存的技术原理及特性解析
  • C 语言问题
  • 基于结构熵权-云模型的铸铁浴缸生产工艺安全评价
  • filezilla出现connected refused的时候排查问题
  • String boot 接入 azure云TTS
  • Java试题-选择题(4)
  • 防火墙相关技术内容
  • JVM 调优中JVM的参数如何起到调优动作?具体案例,G1GC垃圾收集器参数调整建议
  • JVM学习日记(十四)Day14——性能监控与调优(一)
  • 基于ELK Stack的实时日志分析与智能告警实践指南
  • SpringBoot 信用卡检测、OpenAI gym、OCR结合、DICOM图形处理、知识图谱、农业害虫识别实战
  • JVM 01 运行区域
  • Qwen3 Embedding:新一代文本表征与排序模型
  • Hyper-V + Centos stream 9 搭建K8s集群(一)
  • 手动开发一个TCP客户端调试工具(三):工具界面设计
  • 【人工智能agent】--服务器部署PaddleX 的 印章文本识别模型
  • Design Compiler:Milkyway库的创建与使用
  • 分布式微服务--Nacos作为配置中心(补)关于bosststrap.yml与@RefreshScope
  • 集成电路学习:什么是CMSIS微控制器软件接口标准
  • [创业之路-528]:技术成熟度曲线如何指导创业与投资?
  • UNet改进(28):KD Attention增强UNet的知识蒸馏方法详解
  • 深入解析 <component :is> 在 Vue3 组合式中的使用与局限
  • 【推荐100个unity插件】快速实现汽车控制器——PROMETEO: Car Controller插件
  • 除数博弈(动态规划)
  • [硬件电路-124]:模拟电路 - 信号处理电路 - 测量系统的前端电路详解