美文网首页
读书笔记 | More Effective C++ | 基础议题

读书笔记 | More Effective C++ | 基础议题

作者: yiekue | 来源:发表于2017-07-30 18:02 被阅读70次
    c++

    个人阅读《More Effective C++》一书的笔记。

    条款1:仔细区别pointers和references

    • pointer(指针): 可以为空(null),可以重新赋值,使用前需要测试其有效性。
    • refences(引用): 不可为空,必须初始化,无法重新赋值。

    注意:定义一个空指针的引用会引起未定义行为的问题

    当考虑“不指向任何对象”的可能性时,或“不同时间指向不同对象”的能力时,考虑使用指针;确定“总会代表某个对象”而且“一旦确定就不在改变”时,就使用引用。

    一般运算符重载常使用引用。

    条款2:最好使用C++转型操作符

    C++引入了4个新的转型操作符:static_cast,const_cast,dynamic_cast,reinterpret_cast。使用方式是static_cast<type>(expression) 。例如,要将一个 int 型转换为 double 型的值:

    int firstNumber,secondNumber;
    double result = static_cast<double>(firstNumber)/secondNumber;
    
    • static_cast: 基本上具有和旧式类型转换相同的能力和相同的限制。例如不能把struct 转成int 或将double转成指针。

    • const_cast: 可用于切仅可用于改变表达式中的常量性(constness)或易变性(volatileness),如果用于其他用途,转型动作会被拒绝。const_cast最常见的用途是用来去掉某个对象的常量性。例如:

      class Widget {...};
      class SpecialWidget: public Widget {...};
      void update(SpecialWidget *psw);
      SpecialWidget sw;                              //sw是一个non-const对象
      const SpecialWidget& csw=sw;                   //csw是一个sw的const引用
      
      update(&csw);                                  //错误!update函数要求一个const的参数
      
      update(const_cast<SpecialWidget*>(&csw));      //正确!&csw的常量性被取出掉了。
      update((SpecialWidget*)&csw);                  //同上,只是采用了旧式的转型语法。
      
      Widget *pw = new SpecialWidget;
      update(pw);                                      //错误!pw的类型是Widget*,但是update函数要求SpecialWidget*。
      
      update(const_cast<SpecialWidget*>(pw));        //错误!const_cast只能用来改变常量性或变易性,无法进行继承的向下转型(cast down)动作。
      
    • dynamic_cast: 用来指向继承体系中“安全的向下转型或跨系转型动作”。也就是将“指向基类对象(base class objects)的指针或引用”转型为“指向派生类对象的指针或对象”,并返回转型是否成功。如果转型失败,并以一个空指针(当转型对象是指针时),或一个异常(exception)(当转型对象是引用时)表现出来。dynamic_cast只能用于继承体系中,不能应用于缺乏虚函数的类型身上(条款24),也不能改变类型的常量性。例如:

      Widget *pw;
      ...
      update(dynamic_cast<SpecialWidget*>(pw));//正确!
      
    • reinterpret_cast: 这个操作符的转换结果总是和编译平台相关,所以reinterpret_cast不具有移植性。它最常用的用途是转换“函数指针”类型。由于其不具有移植性,所以某些情况这样的转型会导致不正确的结果,所以应该尽量避免将函数指针转型。

    如果编译器没有不支持这些语法,可以使用宏来模仿这些语法:

    #define static_cast(TYPE,EXPR)  ((TYPE)(EXPR))
    #define dynamic_cast(TYPE,EXPR)  ((TYPE)(EXPR))
    #define reinterpret_cast(TYPE,EXPR)  ((TYPE)(EXPR))
    

    条款3:绝对不要以动态(polymorphically)方式处理数组

    继承(inheritance)的最重要的性质之一就是:指向基类对象的指针或引用,可以操作派生类对象。我们说这种指针和引用的行为是多态(polymorphically)的。虽然C++也允许通过基类对象的指针和引用来操作派生类的对象数组,但是程序却几乎无法如预期一样的运行。考虑如下的示例程序:

    class BST {...};
    class BalancedBST {...};
    
    void printBSTArray(ostream& s, const BST array[], int numElements)
    {
      for(int i = 0; i < numElements; ++i){
        s << array[i]; //假设BST对象有一个<<操作符可用
      }
    }
    
    BST BSTArray[10];
    ...
    printBSTArray(cout, BSTArray, 10);//运行良好
    
    BanlancedBST bBSTArray[10];
    ...
    printBSTArray(cout, bBSTArray, 10);//能正确运行吗?
    

    这段程序在编译时是没有问题的,但是运行程序时却很可能不会得到我们预想的结果。问题出在printBSTArray 函数里面的循环中,array[i] 其实是一个指针算术表达式,代表的是*(array+i) ,array是一个指向数组起始的指针。在内存中array+i 所指向的地址和array 所指向的地址相差i*sizeof(数组中对象) ,因为它们之间有i个对象。这里编译器假定array中的对象的大小和BST对象的大小是一致的,所以第一个printBSTArray函数运行良好;但是第二个却会出现问题,一般来说,派生类的具有更多的数据成员,因此派生类对象的大小一般都要比基类对象大,而这个程序中由于函数的参数设置,编译器还是认为对象大小为BST对象的大小。这样通过指针算术表达式算出来的地址就是错误的,从而产生意想不到的结果。

    简单来说,就是多态和指针运算不能混用,数组对象几乎总会涉及到指针运算,因此数组和多态不要混用。

    条款4:避免冗余的 default constructor

    本条款的英文原文为:Avoid gratuitous default constructor

    default constructor是指在没有任何外来信息的情况将对象初始化。有的类不需要外部信息即可完成初始化,例如数字可以默认的初始化为0或者无意义的值,指针可以初始化为null,但是有的类却必须要由外部的信息参与才可以完成初始化,对这种情况,如果没有default constructor将会在三种情况出问题:

    • 产生数组的时候;当产生类的对象数组的时候,将会由于无法调用该类的构造函数而出错。
    • 无法适用于基于模板的容器类(template-based container classes)。对模板而言,被实例化的“目标类型”必须有一个default constructors,因为模板内几乎总是会产生一个以“模板类型参数” 作为类型二架构起来的数组。
    • 虚基类如果缺少default constructor,与之合作将会很痛苦。因为虚基类的构造函数的参数必须有派生层次最深的类提供,这就导致一个缺乏default constructor的虚基类的所有派生类都必须知道这个基类构造函数参数的意义,并提供这个参数。

    由于缺乏default constructor 有这些缺点,所有就有人认为都应该为类提供一个default constructor。但是这样做几乎总是使得类的成员函数变得复杂。同时添加无意义的default constructor 会影响类的效率。

    相关文章

      网友评论

          本文标题:读书笔记 | More Effective C++ | 基础议题

          本文链接:https://www.haomeiwen.com/subject/rldklxtx.html