zoukankan      html  css  js  c++  java
  • 【more effective c++读书笔记】【第4章】效率(3)

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

    1、确保操作符的复合形式(如operator+=)和其独身形式 (如 operator+ )之间的自然关系能够存在,一个好方法就是以前者为基础实现后者。

    例子:

    #include<iostream>
    using namespace std;
    
    class Rational{
    public:
    	Rational(int n = 0, int d = 1) :numerator(n), denominator(d){}//构造函数刻意不为explicit,为了隐式类型转换
    	int getNumerator() const{ return numerator; }
    	int getDenominator() const{ return denominator; }
    	Rational& operator*=(const Rational& rhs){
    		this->numerator *= rhs.numerator;
    		this->denominator *= rhs.denominator;
    		return *this;
    	}
    private:
    	int numerator;
    	int denominator;
    };
    const Rational operator*(const Rational& lhs, const Rational& rhs){//Rational和Rational相乘
    	return Rational(lhs) *= rhs;
    }
    ostream& operator<<(ostream& os, Rational& r){
    	os << r.getNumerator() << '/' << r.getDenominator();
    	return os;
    }
    
    int main(){
    	Rational oneEighth(1, 8);
    	Rational oneFourth(1, 4);
    	Rational oneHalf(1, 2);
    	Rational result = oneHalf*oneFourth*oneEighth;
    	cout << result << endl;
    
    	system("pause");
    	return 0;
    }
    

    3个与效率有关的情况需要注意:

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

    第二,如果同时提供某个操作符的复合形式和独身形式,便允许客户在效率与便利性之间做取舍。客户端可以决定是这样编写:

    Rational a, b, c, d, result;
    ...
    result = a * b * c * d; // 可能用了3个临时对象,每个operator* 调用使用1个
    或是这样编写:
    result = a;  //不用临时对象
    result *= b; // 不用临时对象
    result *= c; //不用临时对象
    result *= d;//不用临时对象
    

    第三,编译器一般对匿名对象进行返回值优化,而对命名对象通常不能。

    const Rational operator*(const Rational& lhs, const Rational& rhs){//Rational和Rational相乘
    	//实现一
    	return Rational(lhs) *= rhs;
    	//实现二
    	Rational result(lhs);
    	return result *= rhs;
    }
    

    上述例子实现二内含一个命名对象result,返回值优化无法施展于此operator*实现代码身上,实现一总是适用返回值优化。


    条款23:考虑使用其他程序库

    考虑iostream和stdio程序库,iostream与stdio相比有几个优点:例如它具有类型安全的特性并且可扩充。然而在效率方面,iostream通常表现得比stdio差,因为stdio的可执行文件通常比iostream更小也更快。

    不同的程序库即使提供相似的技能,也往往表现出不同的性能取舍策略,所以一旦你找出程序的瓶颈,应该思考是否可能通过改用另一个程序库来消除瓶颈。


    条款24:了解virtual functions、multiple inheritance、virtual base classes、runtime typeidentification的成本

    1、虚函数会带来以下成本:

    a、你必须为每个拥有虚函数的class耗费一个virtual talbe空间,其大小视虚函数的个数(包括继承而来的)而定。

    b、你必须在每一个拥有虚函数的对象内付出一个为额外指针的代价,也就是一个virtual table pointer。

    c、你事实上放弃了inlining。因为“inline”意味“在编译期,将调用端的调用动作被调用函数的函数本体取代”,而“virtual”意味“直到运行时期才能知道哪个函数被调用”。

    2、多重继承会更复杂:

    a、找出对象内的vptrs会变得比较复杂。因为一个对象之内会有多个vptrs;除了我们已经讨论过的单独的vtbl以外,针对base classes而形成的特殊vtbls也会被产生出来。因此虚函数对每一个对象和每一个类所造成的空间负担又增加了,运行时期的调用成本也增加了。

    b、多继承经常导致对虚基类的需求。虚基类可能导致另一个成本,因为其实现做法常常利用指针,指向虚基类成分,以消除重复现象,这样可能导致对象内的隐藏指针增加。

    3、RTTI让我们在运行时期获得对象和类的相关信息,这些信息被存储在类型为type_info的对象里,通过typeid操作符可以获取一个类的type_info对象。C++规范书上说,只有当某种类型拥有至少一个虚函数,才保证我们能够检验该类型对象的动态类型。RTTI的设计理念是根据类的vtbl来实现。

    上述内容的具体例子见我的另一篇博客http://blog.csdn.net/ruan875417/article/details/46408475

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    cocos2dx 解释二具体的启动过程:内存管理和回调
    允许Android随着屏幕转动的控制自由转移到任何地方(附demo)
    freemarker 空白处理
    采用Duplicate target database在线恢复秩序oracle datagard图书馆设备
    activiti入门2流程引擎API和服务基础设施
    【android】ImageView的src和background以及两者之间的神奇的差异
    HDU ACM 1007 Quoit Design 分而治之的方法,最近点
    Dynamics CRM2013/2015 禁止欢迎屏幕(Disable the Welcome Screen)
    2013年第42周二明智行动的艺术
    2013第42周一睡前
  • 原文地址:https://www.cnblogs.com/ruan875417/p/4785421.html
Copyright © 2011-2022 走看看