《More Effective C++》读书笔记-效率

条款16-18省略。

19、了解临时对象的来源

临时对象产生的两种条件:

1、为了使函数成功调用而进行隐式类型转换时。
2、函数返回对象时。

临时对象是有开销的,因此要尽可能去消除它们,然而更重要的是训练自己寻找可能建立临时对象的地方,在任何时候只要见到常量引用参数,就存在建立临时对象而绑定在参数上的可能性,在任何时候只要见到函数返回对象,就会有一个临时对象被建立(以后被释放)。

20、协助完成返回值优化

略过。

21、通过重载避免隐式类型转换

隐式类型转换将产生临时对象,从而带来额外的系统开销。

解决办法是使用重载,以避免隐式类型转换。要注意的一点是在C++中有一条规则是每一个重载的operator必须带有一个用户定义类型的参数(这条规定是有道理的,如果没有的话,程序员将能改变预定义的操作,这样做肯定把程序引入混乱的境地)。
另外,牢记8020规则,没有必要实现大量的重载函数,除非有理由确信程序使用重载函数后整体效率会有显著提高。

class UPint  
{  
public:  
    UPint();  
    UPint(int value);  
    ....  
}  
//重载operator+(const UPint& lhs,const UPint& rhs)
const UPint operator+(const UPint& lhs,const UPint& rhs);  

UPint u1,u2;  
UPint u3 = u1 + u2;//都是UPint对象,利用上述重载operator+即可
//以下两种情形也可成功,为什么?因为以下语句执行了隐式类型转换,产生了
//临时对象,并将整数10转换为UPint。
UPint u4 = 10+u2;
UPint u4 = u2+10; 

编译器来执行此类隐式转换,很方便,但是此类转换会产生临时对象,从而产生我们不想要的运行成本。我们并不想进行隐式类型转换,仅仅想要能够对UPint和int进行加法,可以利用重载函数,每个函数拥有不能的参数。如下列所示:

const UPint operator+(cosnt UPint& lhs,int rhs);  
const UPint operator+(int lhs,const UPint& rhs);  

22、考虑以操作符复合形式(op=)取代其独身形式(op)

要确保操作符的复合形式和其独身形式之间的自然关系能够存在,一个好方法就是以复合形式为基础来实现独身形式

Rational& operator+=(const Rational& rhs);//(1)
const Ratinal operator+(const Rational& lhs,const Rational& rhs)  
{//(2)
    return Rational(lhs)+=rhs;  
}  

此例中operator+=是从头做起的,而operator+是调用前者以供应它们所需的机能。如果采用这种设计,那么这些操作符之中就只有复合形式才需要维护。 主要有以下好处:

1、一般而言,复合操作符比独身版本效率高,因为独身版本通常必须返回一个新对象,故必须要负担一个临时对象的构造和析构成本。至于复合版本则是直接将结果写入其左端自变量,所以不需要产生一个临时对象来放置返回值。

2、允许客户在效率和便利之间作取舍。

Rational a,b,c,d,result;  
//(1)独身版本:便利。用到三个临时对象。
result = a+b+c+d;   
//(2)复合版本:效率。都不需要临时对象。 
result=a;  
result+=b;  
result+=c;  
result+=d;  

前者较易撰写、调试、维护。后者效率较高。

3、独身版本的两个实现形式:

template<class T>  
const T operator+(const T& lhs,const T& rhs)  
{  
    return T(lhs) += rhs; //产生临时对象
}    
//version 2
template<class T>  
const T operator+(const T& lhs,const T& rhs)  
{  
    T result(lhs);//产生命名对象
    return result+= rhs;  
}  

第一个版本和第二版本几乎一样,但是却有个重要的差异,第二版本中含有一个命名对象result,而第一版本中拥有返回值优化,使得编译器具有最佳的效果。匿名对象总是比命名对象更容易消除,所以当你面临命名对象和临时对象的抉择时,最好是选择临时对象。它绝不会比其命名对象耗用更多成本,反倒是极有可能降低成本。

23、考虑使用其它程序库

略过。

24、了解 virtual functions、multiple inheritance、virtual base classes、runtime type identification 的成本

1、使用虚函数,会使用所谓的 virtual tables 和 virtual table pointers ,通常简写为 vtbls 和 vptrs 。vtbl通常是一个函数指针的数组或链表,每一个声明或继承虚函数的类都有自己的vtbl,其中的每一个元素就是该类的各个虚函数的指针。

虚函数所需的代价:
(1)必须为每个包含虚函数的类的virtual table留出空间;
(2)每个包含虚函数的类的对象里,必须为额外的指针付出代价;
(3)实际上放弃了使用内联函数,虚函数是运行时绑定的,而 inline 是编译时展开的,即使你对虚函数使用 inline ,编译器也通常会忽略。

2、多继承时,在单个对象里有多个vptr(一个基类对应一个),”找出对象内的vptrs”会变得比较复杂,它和虚基类一样,会增加对象体积的大小。

多重继承往往导致virtual base classes(虚拟基类)的需求。在non-virtual base的情况下,如果派生类对于基类有多条继承路径,那么派生类会有不止一个基类部分,让基类为virtual可以消除基类的数据成员在每一个子类复制滋生。然而虚基类也可能导致另一成本:其实现做法常常利用指针,指向”virtual base class”部分,因此对象内可能出现一个(或多个)这样的指针。例如多重继承的”菱形”结构。

3、RTTI能让我们在运行时找到对象和类的有关信息,所以肯定有某个地方存储了这些信息让我们查询,这些信息被存储在类型为type_info的对象里。通常,RTTI被设计为在类的vbtl上实现。
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值