【读书笔记】【More Effective C++】操作符(Operators)

条款 5:对定制的“类型转换函数”保持警觉

  • 有两种函数允许编译器进行隐式类型转换(隐式类型转换会在意想不到的地方被调用):
    • 单参数构造函数(包括除了第一个参数,所有参数都有缺省值的情况)。
    • 隐式类型转换运算符。【这是一种成员函数,如 operator double() const,该函数将类转为 double,其无需定义返回类型,因为返回类型就是这个函数的名字】
  • 避免方式:
    • 隐式类型转换运算符的避免方式比较简单(因为通常它不是类设计的必需品),用另一个功能对等的函数取代隐式类型转换运算符即可,如 double asDouble();
    • 单参数构造函数的使用往往是无法避免的,而为了避免单参数构造函数被隐式调用,可以在前添加 explicit 关键字,此时只有显式调用该构造函数才会生效,编译器就不会在意想不到的时候调用它。
  • 除了添加 explicit 关键字,还有一种方案可以更高效地避免单参数构造函数带来的隐式转换:【所谓的高效指的是用了这种技术,可以让部分隐式构造非法化】
    • 假设新设计的类如下:
      class Array{
      public:
          class ArraySize{
          public:
              ArraySize(int numElements): theSize(numElements){}
          private:
              int theSize;
          };
          Array(ArraySize size);//接收一个ArraySize对象,而非一个int
          ......
      };
      
    • 此时调用 Array(int) a(10) 来定义对象是允许的,因为编译器知道 ArraySize(int numElements) 可以将 int 转换为一个临时的 ArraySize 对象,而该对象正是 Array(ArraySize size) 需要的。
      • 这样做相当于在使用单一参数构造对象时中间加了一层转换,其中 ArraySize 类被称为 proxy(代理)类,关于代理类的相关内容会在条款 30 中进行讨论。
    • 而下述情况则无法通过:
      bool operator==(const Array<int>& lhs,const Array<int>& rls);
      Array<int> a;
      Array<int> b;
      for(int i=0;i<10;++i)
          if(a==b[i]){}//错
      
    • 在这种情况下,需要两次用户定制转换行为才可以,和前面的情况不一样,编译器无法知道需要两次转换还是多次转换,所以这种情况是被禁止的。

条款 6:区别 increment/decrement 操作符的前置(prefix)和后置(postfix)形式

  • 前置式不带参数,而后置式带一个 int 参数,在调用时,编译器传递一个 0 给函数。【后置式的参数不会被使用,只是用来区分】
    • 前置式先累加后取值,后置式先取出后累加。
  • 前置返回一个引用,而后置返回一个 const 对象
    • 后置式返回对象而不是引用,因为后置式返回的是旧值。
    • 但关键是后置式为什么返回一个 const 变量,这是为了防止 i++++ 这样的式子能够通过编译,因为 i++ 返回一个临时对象,而 i++++ 的第二个后置式会作用在临时对象上,这就导致了 i 实际上只累加了一次。【为了避免这种没必要的误会,于是便将后置式返回 const 变量,这样就能避免第二次后置式的调用(为什么能避免?因为后置式是一个 non-const member function,也就是说不能传入 const 参数)】
  • 一般来说,后置式的实现都需要前置式作为基础,即后置式的实现中一般会调用前置式。

条款 7:千万不要重载 &&、|| 和 , 操作符

  • 如题,不要重载的原因是永远无法模拟它们。
  • C++ 对 && 和 || 采取“骤死式”的评估方式。【对布尔运算的评估方式一般是骤死式】
    • 骤死式(short-circuiting behavior)的意思是:从左到右进行判定,如果左边评估完成,则右边不会进行评估。
    • 然而当重定义这些运算符时,函数调用语义取代骤死式语义。
    • 函数调用语义意味着:所有参数都必须评估完成(即所有参数都必须完成求值),各参数评估的次序未知。
  • C++ 对逗号的评估方式为:逗号左边会先评估,然后逗号的右边再被评估,最后整个逗号表达式的结果以逗号右边的值为代表。
  • 还有一些操作符不允许重载,如 new 操作符不允许重载,但是 operator new 是允许重载的。

条款 8:了解各种不同意义的 new 和 delete

  • new operator(也叫 new 表达式)和 operator new 是不同的。
    • A* a = new A(),这里的 new 是 new operator(后面统称为 new),它是 C++ 内置的,不能改变它的意义(无法重载),它总是先分配足够内存然后调用构造函数初始化内存中的对象
    • new 调用 operator new 来完成第一部分内存的分配步骤,所以允许 operator new 重载就相当于允许程序员自定义分配内存的方式。
  • operator new 通常声明为 void* operator new(size_t size),这里返回类型为 void* 因为函数返回的是一个未初始化内存的指针。
    • 程序员能够增加额外的参数重载函数 operator new,但是第一个参数类型必须是 size_t
  • 而 placement new(又叫做定位 new)和 operator new 不一样,因为 placement new 利用已经分配好的内存来构建对象。【placement new 相当于 operator new 的多一个指针参数的特别版本】
  • delete operator 和 operator delete 也是类似的不同。
    • 要注意 placement operator 不要与 delete 配套使用。
  • 当调用 new 来分配数组时,就不是调用 operator new 来分配内存,而是调用 operator new[] 来分配内存。

你可能感兴趣的:(#,More,Effective,C++,c++)