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

Item20--宁以 pass-by-reference-to-const 替换 pass-by-value

1. 性能代价:为什么要避免 Pass-by-Value?

在 C++ 中,默认情况下函数参数是按值传递(pass-by-value)的。这意味着当你在函数中传递一个对象时,编译器会调用该对象的拷贝构造函数(Copy Constructor)来制作一个副本。

场景模拟

假设你有以下类继承结构:

class Person {
public:Person();           // 1. Person 构造virtual ~Person();  // 2. Person 析构// ... private string name, string address ...
};class Student : public Person {
public:Student();          // 3. Student 构造~Student();         // 4. Student 析构// ... private string schoolName, string schoolAddress ...
};

现在有一个函数用于验证学生信息,错误(低效)的写法如下:

bool validateStudent(Student s); // 参数是 pass-by-value// 调用
Student plato;
bool platoIsOK = validateStudent(plato);

发生了什么?

validateStudent(plato) 被调用时,为了创建参数 s,系统发生了巨大的开销:

  1. 调用 Student 的 Copy Constructor:将 plato 复制给 s
  2. 调用 Person 的 Copy Constructor:作为基类部分。
  3. 成员对象的复制PersonStudent 内部可能有 std::string 对象(如 name, address 等),每一个 string 都要调用拷贝构造。
  4. 析构函数:函数结束时,s 被销毁,上述所有对象的析构函数会被再次调用。

总代价 = 6 次构造函数调用 + 6 次析构函数调用(假设每个类有2个 string)。而这一切仅仅是为了让函数“看一眼”这个对象。

解决方案:Pass-by-Reference-to-Const

// 正确写法
bool validateStudent(const Student& s);
  • const: 保证函数内部不会修改原来的对象(调用者放心)。
  • & (引用): 实际上只是传递了一个指针(地址)。
  • 开销: 没有构造函数,没有析构函数。无论对象多大,代价几乎为零。

2. 正确性问题:对象切割 (The Slicing Problem)

这是 pass-by-value 最危险的陷阱。当一个派生类对象值传递的方式传入一个接受基类对象的函数时,派生类的特有部分会被“切掉”(Slicing)。

场景模拟

class Window {
public:string name() const; // 返回窗口名称virtual void display() const; // 虚函数,用于显示
};class WindowWithScrollBars : public Window {
public:virtual void display() const override; //以此实现带有滚动条的显示
};

错误的函数写法

// 这里的参数是 Window (By Value)
void printNameAndDisplay(Window w) {std::cout << w.name();w.display();
}// 调用
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);

发生了什么?

  1. 参数 w 是一个 Window 对象。
  2. 当你传入 wwsb 时,编译器调用 Window 的拷贝构造函数。
  3. 它只复制了 wwsb 中属于 Window 的那部分数据。
  4. 结果:函数内部的 w.display() 永远只会调用 Window::display(),而不是 WindowWithScrollBars::display()。多态失效了。

解决方案

// 正确写法:接受 const 引用
void printNameAndDisplay(const Window& w) {std::cout << w.name();w.display();
}

因为传递的是引用(在底层通常是指针),w 指向了完整的 WindowWithScrollBars 对象。调用 w.display() 时,虚函数机制(vptr/vtable)能正常工作,正确调用派生类的版本。


3. 特例:什么时候该用 Pass-by-Value?

Item 20 并不是说永远都要用引用。对于某些小型的、类似内置类型的对象,Pass-by-value 更加高效。

适用 Pass-by-Value 的情况:

  1. 内置类型int, double, char, bool 等。
    • 理由:把一个 int 放入寄存器的速度比通过引用(指针解引用)访问内存要快得多。
  2. STL 迭代器 (Iterators)函数对象 (Function Objects)
    • 理由:习惯上它们就被设计为通过值传递。因此我们在设计迭代器时,要确保它们的拷贝代价很小且没有切割问题。

总结 (Summary)

特性 Pass-by-Value Pass-by-Reference-to-Const
效率 低(大型对象需拷贝构造+析构) 极高(无新对象生成)
多态 失效(发生对象切割) 正常(支持多态)
适用场景 内置类型 (int, double), STL迭代器 用户自定义类 (Classes, Structs)

核心法则:

对于用户自定义的类型(User-defined types),请总是首选 pass-by-reference-to-const

http://www.jsqmd.com/news/115953/

相关文章:

  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • 3433. 统计用户被提及情况
  • Item19--设计 class 犹如设计 type
  • 国外软件,安装即时专业版!
  • Item19--设计 class 犹如设计 type
  • basic_regex
  • c++狼人杀
  • 宠物识别丨基于弱监督学习的宠物视频内容自动标注技术实践 - 指南
  • 朴易天下:道家修行的专业术语分享
  • 个人投资者的落地路径:从“说人话,做量化”到实盘前的三道关
  • 神经网络中的 block 和 module
  • item13--使用对象管理资源
  • 深入解析:蓝桥杯基础算法精讲:模拟与高精度运算实战指南
  • item12-- 拷贝一个对象的所有组成部分
  • sub_match
  • sub_match
  • 抽奖机随机号码生成:3 种算法实现 + 测试全解析(附完整代码)
  • 【零基础精通】Python 字符串全解析:从字符序列到不可变对象的深度构建
  • item14--谨慎考虑资源管理类的拷贝行为
  • python django flask酒店客房管理系统数据可视化分析系统_gq8885n3--论文md5
  • python django flask鹿幸公司员工食堂在线点餐餐饮餐桌预约管理系统的设计与实现_utcnqqs0--论文
  • error_code
  • 虚拟化初步了解
  • Miloco 深度打通 Home Assistant,实现设备级精准控制
  • 好用的大型牛场水滴粉碎机技术强的
  • set_value
  • 日记1217
  • function的类型擦除
  • function bind