• More Effective C++精简版(4续)


    6.5
          从效率的观点来看,你不应该关心函数返回的对象,你仅仅应该关心对象的开销。

    inline const Rational operator*(const Rational& lhs,

                                    const Rational& rhs)

    {

     return Rational(lhs.numerator() * rhs.numerator(),

                      lhs.denominator() * rhs.denominator());

    }

    Rational的构造函数被调用, 这是一个临时对象,函数把它拷贝给函数的返回值

    C++规则允许编译器优化不出现的临时对象(temporary objects out of existence)。因此如果你在如下的环境里调用operator*:

    Rational a = 10;

    Rational b(1, 2);

    Rational c = a * b;                          // 在这里调用operator*

             编译器就会被允许消除在operator*内的临时变量和operator*返回的临时变量。它们能在为目标c分配的内存里构造return表达式定义的对象。如果你的编译器这样去做,调用operator*的临时对象的开销就是零:没有建立临时对象。你的代价就是调用一个构造函数――建立c时调用的构造函数。而且你不能比这做得更好了,因为c是命名对象,命名对象不能被消除

            它甚至还有一个名字:返回值优化. 但注意,这种优化对普通的赋值运算无效,编译器不能够用拷贝构造函数取代赋值运算动作.
     

    6.6

             用函数重载来消除类型转换

             C++中有一条规则是每一个重载的operator必须带有一个用户定义类型(user-defined type)的参数

             利用重载避免临时对象的方法不只是用在operator函数上。比如在大多数程序中,你想允许在所有能使用string对象的地方,也一样可以使用char*,反之亦然。同样如果你正在使用numerical(数字)类,例如complex(参见条款M35),你想让int和double这样的类型可以使用在numerical对象的任何地方。因此任何带有string、char*、complex参数的函数可以采用重载方式来消除类型转换。

              不过,必须谨记80-20规则,没有必要实现大量的重载函数

    6.7

             operator+、operator=和operator+=之间没有任何关系,因此如果你想让这三个operator同时存在并具有你所期望的关系,就必须自己实现它们。同理,operator -, *, /, 等等也一样。

             确保operator的赋值形式(assignment version)(例如operator+=)与一个operator的单独形式(stand-alone)(例如 operator+ )之间存在正常的关系,一种好方法是后者(指operator+)根据前者(指operator+=)来实现

    第一、   总的来说operator的赋值形式比其单独形式效率更高,因为单独形式要返回一个新对象,从而在临时对象的构造和释放上有一些开销

    第二、   提供operator的赋值形式的同时也要提供其标准形式,允许类的客户端在便利与效率上做出折衷选择

    第三、   注意使用返回值优化

    template<class T>

    const T operator+(const T& lhs, const T& rhs)

    {

     T result(lhs);                        // 无法享受编译器的优化

     return result += rhs;                

    }
     

    6.8
         一旦你找到软件的瓶颈(通过进行profile 参见条款M16),你应该知道是否可能通过替换程序库来消除瓶颈

         比如如果你的程序有I/O瓶颈,你可以考虑用stdio替代iostream,如果程序在动态分配和释放内存上使用了大量时间,你可以想想是否有其他的operator new operator delete的实现可用。因为不同的程序库在效率、可扩展性、移植性、类型安全和其他一些领域上蕴含着不同的设计理念,通过变换使用给予性能更多考虑的程序库,你有时可以大幅度地提高软件的效率。

    6.9
            在程序中的每个类只要声明了虚函数或继承了虚函数,它就有自己的vtbl,并且类中vtbl的项目是指向虚函数实现体的指针

            如果有一个C2类继承自C1,重新定义了它继承的一些虚函数,那么它的virtual table项目指向与对象相适合的函数

    虚函数所需的第一个代价:你必须为每个包含虚函数的类的virtual talbe留出空间

                            第二个代价是:在每个包含虚函数的类的对象里,你必须为额外的指针付出代价。

    调用虚函数所需的代价基本上与通过函数指针调用函数一样。虚函数本身通常不是性能的瓶颈。

    在实际运行中,虚函数所需的代价与内联函数有关。实际上虚函数不能是内联的。这是因为“内联”是指“在编译期间用被调用的函数体本身来代替函数调用的指令,”但是虚函数的“虚”是指“直到运行时才能知道要调用的是哪一个函数。”

                            第三个代价:你实际上放弃了使用内联函数

    多继承经常导致对虚基类的需求

    对象和类的有关信息,存储在类型为type_info的对象里,使用typeid操作符访问一个类的type_info对象。

    下面这个表各是对虚函数、多继承、虚基类以及RTTI所需主要代价的总结:

    Feature

    Increases
    Size of Objects

    Increases
    Per-Class Data

    Reduces
    Inlining

    Virtual Functions

    Yes

    Yes

    Yes

    Multiple Inheritance

    Yes

    Yes

    No

    Virtual Base Classes

    Often

    Sometimes

    No

    RTTI

    No

    Yes

    No

  • 相关阅读:
    VBA 声明 Option Explicit,让代码更规范
    VBA 声明 Option Explicit,让代码更规范
    VBA 静态变量 全局变量
    VBA 静态变量 全局变量
    VBA 设置单元格格式
    VBA 设置单元格格式
    正则表达式捕获性分组,非捕获性分组,前瞻,后瞻
    正则表达式捕获性分组,非捕获性分组,前瞻,后瞻
    vba 清除本sheet所有单元格内容和清除所有sheet中所有单元格
    vba 清除本sheet所有单元格内容和清除所有sheet中所有单元格
  • 原文地址:https://www.cnblogs.com/ethan/p/675748.html
Copyright © 2020-2023  润新知