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

Effective C++ 条款20:宁以pass-by-reference-to-const替换pass-by-value

Effective C++ 条款20:宁以pass-by-reference-to-const替换pass-by-value


核心思想优先使用const T&传递参数代替值传递,避免昂贵的拷贝开销并防止对象切割问题,同时保持参数不可修改的安全性。

⚠️ 1. 值传递的代价与陷阱

昂贵拷贝开销

class BigObject {
public:BigObject();                   // 构造耗时BigObject(const BigObject&);   // 拷贝昂贵(含10个vector成员)~BigObject();
private:std::vector<double> data1, data2, ...;
};// 值传递:触发完整拷贝
void process(BigObject bo);        // 每次调用拷贝构造+析构process(BigObject());              // 拷贝成本:O(n) * 10

对象切割问题

class Window {
public:virtual void display() const;  // 基类虚函数
};class SpecialWindow : public Window {
public:void display() const override; // 派生类实现
};// 值传递导致派生类被切割
void showWindow(Window w) {        // 参数类型为基类w.display();                   // 总是调用Window::display()
}SpecialWindow sw;
showWindow(sw);  // 传入SpecialWindow → 被切割为Window对象

🚨 2. 解决方案:const引用传递

高效传递大对象

// 引用传递:零拷贝开销
void process(const BigObject& bo);  // 仅传递指针大小的引用BigObject obj;
process(obj);  // 无拷贝构造/析构调用

保持多态完整性

// 引用传递保持对象完整类型
void showWindow(const Window& w) {  // 参数为基类引用w.display();                    // 动态绑定正确调用派生类方法
}SpecialWindow sw;
showWindow(sw);  // 调用SpecialWindow::display()

⚖️ 3. 关键原则与例外情况
传递方式适用场景性能影响
const T&自定义类型、类层次结构中的对象零拷贝开销
值传递内置类型、小型POD结构拷贝成本可忽略
移动语义(T&&)需要转移所有权的可修改对象转移资源所有权
智能指针需要共享所有权的对象引用计数操作

内置类型值传递例外

// 内置类型直接传值更高效
void validate(int x, float y);    // 比const int&更高效// 小型POD结构(<16字节)
struct Point2D { float x, y; };
void translate(Point2D p);        // 寄存器传递可能更快

STL迭代器和函数对象

// 迭代器设计为值传递
template<typename InputIt>
void processRange(InputIt begin, InputIt end);  // 按值传递迭代器// 函数对象可能按值传递
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // lambda值传递

现代C++扩展规则

// 通用引用模板(C++11)
template<typename T>
void forwardExample(T&& arg);     // 完美转发参数// 移动语义优化
void sinkExample(BigObject&& bo); // 明确转移所有权

💡 关键原则总结

  1. 默认传递策略
    • 自定义类型 → const T&
    • 内置类型 → 直接传值
  2. 多态保护原则
    • 基类类型参数必须使用引用传递
    • 避免对象切割(slicing problem)
  3. 性能临界区优化
    • 小型结构体实测性能对比
    • 高频调用场景考虑内联
  4. 现代C++增强
    • 所有权转移使用T&&
    • 完美转发使用T&& + std::forward

值传递陷阱重现

class Employee {
public:virtual int calculateSalary() const;
};class Manager : public Employee {int calculateSalary() const override; // 更高计算复杂度
};// 危险:值传递导致切割+性能双问题
void processEmployee(Employee e) {int salary = e.calculateSalary(); // 1. 调用基类方法
}                                    // 2. 触发完整拷贝Manager m;
processEmployee(m); // 对象被切割,额外拷贝基类部分

安全重构方案

// 解决方案1:const引用传递(推荐)
void processEmployee(const Employee& e) {int salary = e.calculateSalary(); // 动态绑定正确
}                                    // 无拷贝开销// 解决方案2:智能指针传递(共享所有权)
void processEmployee(std::shared_ptr<const Employee> e);// 解决方案3:移动语义(转移所有权)
void takeOwnershipEmployee(Employee&& e);// 解决方案4:模板泛型(C++17)
template<typename T, typename = std::enable_if_t<std::is_base_of_v<Employee, T>>>
void processTemplate(const T& emp);
http://www.dtcms.com/a/313475.html

相关文章:

  • LeetCode 2122.还原原数组
  • centos7安装桌面客户软件并远程连接
  • 学习笔记《区块链技术与应用》第五天 分叉
  • Matlab 高斯牛顿法拟合曲线
  • 力扣-200.岛屿数量
  • 01数据结构-二叉搜索树
  • PAT 甲级题目讲解:1012《The Best Rank》
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-55,(知识点:STM32,外设及其特点)
  • 力扣-124.二叉树中的最大路径和
  • LLM调研
  • 计算用户日活:从数据设计到可视化的全流程(高频场景题)
  • 秋招笔记-8.3
  • AUTOSAR进阶图解==>AUTOSAR_RS_SystemTemplate
  • 元宇宙三维化会给游戏行业带来哪些改变?
  • 什么是列存储(Columnar Storage)?深度解析其原理与应用场景
  • 常见的深度学习模块/操作中的维度约定(系统性总结)
  • io_cancel系统调用及示例
  • e2studio开发RA4M2(6)----GPIO外部中断(IRQ)配置
  • 算法题(181):最大直方矩形面积
  • datasets库 的map方法num_proc=16并行踩坑
  • Java 中的 final 关键字有哪些用法?
  • C++ 虚函数相关问题 **
  • 基于云模型的模糊综合风险评估Matlab代码
  • 网关与路由器的区别
  • access系统调用及示例
  • 延迟任务方案-DelayQueue
  • SpringBoot 2.x 升 3.x 避坑指南:企业级项目的实战问题与解决方案
  • Celery-分布式任务队列
  • MySQL深度理解-MySQL锁机制
  • 数据结构学习(day01)