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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值