




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C+箴言:用传引用给 const 取代传值缺省情况下, C+ 以传值方式将对象传入或传出函数(这是一个从 C 继承来的 特性。除非你特别指定其它方式,否则函数的参数就会以实际参数(actual argument 的拷贝进行初始化,而函数的调用者会收到函数返回值的一个拷贝。 这个拷贝由对象的拷贝构造函数生成。这就使得传值(pass-by-value 成为一 个代价不菲的操作。例如,考虑下面这个类层级结构:class Person public:Person(; / parameters omitted for simplicityvirtual Person(; / see Item 7 for
2、 why this is virtual.private:std:string name;std:string address;class Student: public Person public:Student(; / parameters again omittedStudent(;.private:std:string schoolName;std:string schoolAddress;现在, 考虑以下代码, 在此我们调用一个函数 validateStudent, 它得 到一个 Student 参数(以传值的方式,并返回它是否验证有效的结果:bool validateStudent
3、(Student s; / function taking a Student/ by valueStudent plato; / Plato studied under Socratesbool platoIsOK = validateStudent(plato; / call the function当这个函数被调用时会发生什么呢?很明显, Student 的拷贝构造函数被调用,用 plato 来初始化参数 s。同 样明显的是,当 validateStudent 返回时, s 就会被销毁。所以这个函数的参 数传递代价是一次 Student 的拷贝构造函数的调用和一次 Student 的析构
4、函 数的调用。但这还不是全部。一个 Student 对象内部包含两个 string 对象,所以每 次你构造一个 Student 对象的时候,你也必须构造两个 string 对象。一个 S tudent 对象还要从一个 Person 对象继承, 所以每次你构造一个 Student 对象 的时候, 你也必须构造一个 Person 对象。 一个 Person 对象内部又包含两个额 外的 string 对象, 所以每个 Person 的构造也承担着另外两个 string 的构造。 最终, 以传值方式传递一个 Student 对象的后果就是引起一次 Student 的拷贝 构造函数的调用, 一次 Per
5、son 的拷贝构造函数的调用, 以及四次 string 的拷 贝构造函数调用。当 Student 对象的拷贝被销毁时,每一个构造函数的调用都 对应一个析构函数的调用,所以以传值方式传递一个 Student 的全部代价是六 个构造函数和六个析构函数!好了, 这是正确的和值得的行为。 毕竟, 你希望你的全部对象都得到可靠的 初始化和销毁。尽管如此,如果有一种办法可以绕过所有这些构造和析构过程, 应该变得更好,这就是:传引用给 const(pass by reference-to-const:bool validateStudent(const Student& s;这样做非常有效:没有任何
6、构造函数和析构函数被调用, 因为没有新的对象 被构造。 被修改的参数声明中的 const 是非常重要的。 validateStudent 的最 初版本接受一个 Student 值参数,所以调用者知道它们屏蔽了函数对它们传入 的 Student 的任何可能的改变; validateStudent 也只能改变它的一个拷贝。 现在 Student 以引用方式传递, 同时将它声明为 const 是必要的, 否则调用者 必然担心 validateStudent 改变了它们传入的 Student。以传引用方式传递参数还可以避免切断问题(slicing problem。当一个 派生类对象作为一个基类对象被传
7、递 (传值方式 , 基类的拷贝构造函数被调用, 而那些使得对象的行为像一个派生类对象的特殊特性被 “切断” 了。 你只剩下一 个纯粹的基类对象这没什么可吃惊的, 因为是一个基类的构造函数创建了它。 这几乎绝不是你希望的。 例如, 假设你在一组实现一个图形窗口系统的类上工作:class Window public:.std:string name( const; / return name of windowvirtual void display( const; / draw window and contents;class WindowWithScrollBars: public Wind
8、ow public:.virtual void display( const;所有 Window 对象都有一个名字, 你能通过 name 函数得到它, 而且所有的 窗口都可以显示, 你可一个通过调用 display 函数来做到这一点。 display 为 virtual 的事实清楚地告诉你:一个纯粹的基类的 Window 对象的显示方法有可 能不同于专门的 WindowWithScrollBars 对象的显示方法。现在,假设你想写一个函数打印出一个窗口的名字,并随后显示这个窗口。 以下这个函数的写法是错误的:void printNameAndDisplay(Window w / incorre
9、ct! parameter/ may be sliced!std:cout << (;w.display(;考虑当你用一个 WindowWithScrollBars 对象调用这个函数时会发生什么: WindowWithScrollBars wwsb;printNameAndDisplay(wwsb;参数 w 将被作为一个 Window 对象构造它是被传值的,记得吗?而且 使 wwsb 表现得像一个 WindowWithScrollBars 对象的特殊信息都被切断了。 在 printNameAndDisplay 中,全然不顾传递给函数的那个对象的类型, w 将始终 表现得
10、像一个 Window 类的对象 (因为它就是一个 Window 类的对象 。 特别是, 在 printNameAndDisplay 中调用 display 将总是调用 Window:display, 绝不 会是 WindowWithScrollBars:display。绕过切断问题的方法就是以传引用给 const 的方式传递 w:void printNameAndDisplay(const Window& w / fine, parameter wont / be slicedstd:cout << (;w.display(;现在 w 将表现得像实际传入的那种窗
11、口。如果你掀开编译器的盖头偷看一下, 你会发现用指针实现引用是非常典型的 做法, 所以以引用传递某物实际上通常意味着传递一个指针。 由此可以得出结论, 如果你有一个内建类型的对象 (例如, 一个 int, 以传值方式传递它常常比传 引用方式更高效。那么,对于内建类型,当你需要在传值和传引用给 const 之 间做一个选择时,没有道理不选择传值。同样的建议也适用于 STL 中的迭代器 (iterators 和函数对象(function objects,因为,作为惯例,它们就是 为传值设计的。迭代器(iterators 和函数对象(function objects的实现 有责任保证拷贝的高效并且不
12、受切断问题的影响。(这是一个“规则如何变化, 依赖于你使用 C+ 的哪一个部分”的实例。内建类型很小, 所以有人就断定所有的小类型都是传值的上等候选者, 即使 它们是用户定义的。 这样的推论是不可靠的。 仅仅因为一个对象小, 并不意味着 调用它的拷贝构造函数就是廉价的。很多对象大多数 STL 容器也在其中 容纳的和指针一样, 但是拷贝这样的对象必须同时拷贝它们指向的每一样东西。 那可能是非常昂贵的。即使当一个小对象有一个廉价的拷贝构造函数, 也会存在性能问题。 一些编 译器对内建类型和用户定义类型并不一视同仁, 即使他们有同样的底层表示。 例 如, 一些编译器拒绝将仅由一个 double 组成的对象放入一个寄存器中, 即使在 常规上它们非常愿意将一个纯粹的 double 放入那里。 如果发生了这种事情, 你 以传引用方式传递这样的对象更好一些, 因为编译器理所当然会将一个指针 (引 用的实现放入寄存器。小的用户定义类型不一定是传值的上等候选者的另一个原因是:作为用户定 义类型,它的大小常常变化。一个现在较小的类型在将来版本中可能变得更大, 因为它的内部实现可能会变化。甚至当你换了一个不同的 C+ 实现时,事情都 可能会变化。 例如, 就在我这样写的时候, 一些标准库的 string 类型的实现的 大小就是另外一些实现的七倍。
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年房地产项目智能家居系统集成合同
- 二零二五年度5G通信产业合作补充协议合同范本
- 2025年超声诊断中心打字员劳动合同范本
- 二零二五年度厨师职业认证考试与培训合同
- 二零二五餐饮业冷链物流配送及仓储管理合同
- 2025顶管工程管道施工与设备安装承包合同
- 二零二五年度全球生物技术专利申请代理合同ch4
- 二零二五年交通枢纽保安劳务派遣合作协议
- 2025版股权整体转让合同范本:企业股权交易合同签订与执行指南
- 2025版个人土地征收安置补偿合同
- 企业环境保护工作课件
- 2024年云南省富源县人民医院公开招聘护理工作人员试题带答案详解
- 太阳能路灯设计与安装方案
- 2025年高考新课标I卷听力讲评课件-高考英语一轮复习专项
- 2025国家保安员资格考试题库及答案
- 轻量化渲染方案-洞察及研究
- 二甲基亚砜项目可行性研究报告
- 医药代表商务礼仪培训课程
- 2025年深圳中考化学试卷真题(含答案)
- 2025至2030中国游乐类主题公园行业产业运行态势及投资规划深度研究报告
- 医疗机构医疗质量安全专项整治行动方案2025
评论
0/150
提交评论