【Effective C++】设计与声明——reference篇
以pass-by-reference-to-const替换pass-by-value
缺省情况下C++以by value方式传递对象至(或来自)函数。除非你另外指定,否则函数参数都是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回的一个复件,这些复件(副本)由对象的copy构造函数产出,这可能使得pass-by-value成为昂贵的(费时的)操作。
好处1:高效,避免副本对象的创建
考虑以下的继承体系:
class Person{
public:
Person();
virtual ~Person();
private:
string name;
string address;
};
class Student:public Person{
public:
Student();
~Student();
private:
string schoolName;
string schoolAddress;
};
现在考虑以下代码,
bool validateStudent(Student s); //函数需要一个student实参(by value)并返回它是否有效
Student plato;
bool IsOK = validateStudent(plato); //调用函数
在调用函数时,Student的copy构造函数会被调用,以plato为蓝本将s初始化,在函数返回时,s又会被销毁。因此对此函数而言,参数的传递成本是“一次Student Copy构造函数调用,加上一次Student析构函数调用”。注意,Student对象内有两个string对象,所以每次构造一个Student对象也就构造了两个string对象。此外,Student对象继承自Person对象,所以每次构造Student对象也必须构造出一个Person对象,Person对象又有两个string对象在其中。。。因此以by-value方式传递一个Student对象,总体成本是“六次构造函数和六次析构函数”!
如果传递对象的引用,高效的多:
bool validateStudent(const Student& s);
没有新的对象被创建,const也会保证不对s做任何修改。
好处2:避免对象切割问题
当一个derived class对象以value方式传递并视为一个base class对象,base class的copy构造函数会被调用,derived class对象的特性会被全部切割掉:
class Person{
public:
...
string name() const;
};
class Student:public Person{
public:
...
string name() const;
};
调用:
void show(Person p){
cout<<p.name();
}
Student s;
show(s);
此时调用的是Person::name()。解决切割问题的方法是以引用传递参数:
void show(const Person& p)
注意:内置类型,STL的迭代器和函数对象,往往pass-by-value比较适当。
必须返回对象时,别妄想返回其reference
考虑有理数Rational,有个友元操作符*,返回Rational对象。
Rational operator*( const Rational &lhs, const Rational &rhs ) {
Rational result = ...
return result;
}
返回对象,导致临时对象的构造,析构。效率低,因此会想返回方法内局部对象的引用,这种方法不可行,为什么?
1. 定义一个局部变量,就是在stack空间创建对象,方法执行完毕,局部对象销毁。假如返回方法内局部对象的引用,方法执行完,局部对象销毁,这时候,引用指向一堆垃圾。
2.或者在heap上构造一个对象,返回引用。这也不可行,首先,要求客户端负责delete,这不合理。其次,退一步说,就算客户负责,执行delete。但是有些情况,客户无法执行delete。比如:Rational d = a*b*c; a*b的结果没有暴露出来,客户无法执行delete。
3. 既然方法内的局部对象会销毁,你可能会想返回一个reference指向方法内的local static对象,但是这会导致下面的诡异情况:无论何时,a*b == c*d 总是为真。为什么?
local static对象只初始化一次,a*b修改local static对象的值,并返回其引用,c*d也修改local static对象的值,并返回其引用。它们是同一个对象的别名,当然永远相等。只不过,后一次的修改覆盖了前一个修改。
因此,对于这种情况,就让方法返回对象。为了正确的行为,必须付出一定代价。需要说明的是,通过RVO技术,编译器可以进行优化,避免这种代价。
结论:绝不要返回pointer或reference指向一个local stack对象,返回reference指向一个heap-allocated对象,返回pointer或reference指向一个local static对象而可能同时需要多个这样的对象。
【Effective C++】设计与声明——reference篇的更多相关文章
- Effective C++ ——设计与声明
条款18:让接口更容易的被使用,不易误用 接口设计主要是给应用接口的人使用的,他们可能不是接口的设计者,这样作为接口的设计者就要对接口的定义更加易懂,让使用者不宜发生误用,例如对于一个时间类: cla ...
- Effective C++ —— 设计与声明(四)
条款18 : 让接口容易被正确使用,不易被误用 欲开发一个“容易被正确使用,不容易被误用”的接口,首先必须考虑客户可能做出什么样的错误操作. 1. 明智而审慎地导入新类型对预防“接口被误用”有神奇疗 ...
- 《Effective C++》第4章 设计与声明(1)-读书笔记
章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...
- 《Effective C++》第4章 设计与声明(2)-读书笔记
章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...
- 《Effective C++》阅读总结(四): 设计、声明与实现
第四章: 设计与声明 18. 让接口更容易被正确使用,不易被误用 将你的class的public接口设计的符合class所扮演的角色,必要时不仅对传参类型限制,还对传参的值域进一步限制. 19. 设计 ...
- EffectiveC++ 第4章 设计与声明
我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter4 设计与声明 Designs and Declarat ...
- [Effective Java] 创建和销毁对象篇
[Effective Java] 创建和销毁对象篇 1. 优先考虑用静态工厂方法代替构造器 优点: - 静态工厂方法相比于构造器,它们有名称 - 不需要每次在使用的时候创建一个对象 - 可以返回原返回 ...
- 【学习记录】第一章 数据库设计-《SQL Server数据库设计和开发基础篇视频课程》
一.课程笔记 1.1 软件开发周期 (1)需求分析阶段 分析客户的业务和数据处理需求. (2)概要设计阶段 设计数据库的E-R模型图,确认需求信息的正确和完整. /* E-R图:实体-关系图(Ent ...
- Effective C++笔记:设计与声明
条款18:让接口容易被正确使用,不易被误用 1,好的接口很容易被正确使用,不容易被误用.你应该在你的所有接口中努力达成这些性质. 2,“促进正使用”的办法包括接口的一致性,以及与内置类型的行为兼容. ...
- Effective C++笔记04:设计与声明
条款18:让接口easy被正确使用,不易被误用 1,好的接口非常easy被正确使用,不easy被误用.你应该在你的全部接口中努力达成这些性质. 2,"促进正使用"的办法包含接口的一 ...
随机推荐
- Pytorch-tensor的创建,索引,切片
1.基本概念 标量:就是一个数,是0维的,只有大小,没有方向 向量:是1*n的一列数,是1维的,有大小,也有方向 张量:是n*n的一堆数,是2维的,n个向量合并而成 2.a.size(),a.shap ...
- PolarDB-X 如何做分布式数据库热点分析
简介: PolarDB-X 是一款计算存储分离的云原生分布式数据库,在PolarDB-X 2.0的AUTO模式下,数据库会按照表的主键自动Hash分区,将数据均匀的分布到各个数据节点中,最理想的情况是 ...
- 开箱即用!Linux 内核首个原生支持,让你的容器体验飞起来!| 龙蜥技术
简介: 本文将从 Nydus 架构回顾.RAFS v6 镜像格式和 EROFS over Fscache 按需加载技术三个角度来分别介绍这一技术的演变历程. 文/阿里云内核存储团队,龙蜥社区高性能存储 ...
- Apache Flink 在汽车之家的应用与实践
简介: 汽车之家如何基于 Flink 上线了 AutoStream 平台并持续打磨. 本文整理自汽车之家实时计算平台负责人邸星星在 Flink Forward Asia 2020 分享的议题< ...
- Flink 在 58 同城的应用与实践
简介: 58 同城的实时 SQL 建设以及如何从 Storm 迁移至 Flink. 本文整理自 58 同城实时计算平台负责人冯海涛在 Flink Forward Asia 2020 分享的议题< ...
- h5开发,原生开发,混合开发
这里做一点对h5开发,原生开发,混合开发的笔记,记一点更新一点: 一.h5开发:html,css和js编写页面和业务逻辑. 1..页面栈上,h5通过history来管理回退或者前进.vue通过配置路由 ...
- c#胖东来小程序自动购物程序(接单,windows桌面程序、linux程序、网络应用等等)
一.程序效果 自动打开胖东来小程序,自动购物 二.实现 先截屏,然后利用opencv库识别下一步按键所在位置,然后使用mouse_event控制鼠标,模拟人的动作 第一步,截取屏幕 static Bi ...
- Surge多配置文件聚合配置方法
目录 摘要 1. Surge配置原理 2. Surge托管配置 3. Surge多配置文件聚合配置 (1)找到配置文件 (2)编辑配置文件 参考 摘要 Surge 是一个在 macOS 和iOS 平台 ...
- Unity Visual Scripting 使用随记
1.Wait Until并不会再执行前面的代码,而是反复执行获取bool变量的代码:需自己拆出来写. 2.yield return null对应Wait For Next Frame,多用这个避免协程 ...
- MQ消息积压,把我整吐血了
前言 我之前在一家餐饮公司待过两年,每天中午和晚上用餐高峰期,系统的并发量不容小觑. 为了保险起见,公司规定各部门都要在吃饭的时间轮流值班,防止出现线上问题时能够及时处理. 我当时在后厨显示系统团队, ...