• effective C++ 读书精华笔记提取



    让自己习惯c++

    尽可能的使用const

    1. 当const修饰类成员函数时,如果两个函数只有常量性不同, 可以被重载。(本质上,const其实修饰的是this指针, 所以呢, 本质上还是参数的类型不同导致的重载)
    2. 如果要修改一个const的类对象内的成员变量, 需要把变量使用mutable修饰成员变量。也就是说使用mutable修饰的变量不受外层类对象状态的影响。
      struct Data {
      	int a;
      	mutable int b;
      };
      
      const Data data{1, 2};
      data.b = 100;
      
    3. bitwise constness 是C++对常量性的定义。 也就是说,对于const修改的对象,对象本身的内存的占的空间是不可以修改的。例如:假设对象里有指针,指针本身不可以改变,但是指针指向的对象是可以修改的。

    确定对象使用前被初始化

    1. 对于初始化列表:

      1 . C++规定,对象的成员变量的初始化动作发生在进入构造函数体之前, 在初始化列表中完成初始化动作。
      2. 对于内置类型,初始化列表与赋值的成本是相同的。,但是对于其它类型,它们的效率差异很大。
      3. 对于类内的const成员变量或者引用,必须在成员初始化列表中进行初始化。

    2. C++的成员初始化顺序:

      1. 基类对象更早于其子类对象进行初始化。
      2. 同一个类对象内的成员变量,总是以其声明的次序进行初始化,与初始化列表中的顺序无关。
    3. C++对定义于不同的编译单元内的non-local static 对象的初始化相对次序没有明确的定义。

    构造、析构和赋值运算

    C++默认编写和调用的函数:

    1. 默认的构造函数 :
      • 当用户不定义任何构造函数时(包含拷贝构造函数),编译器才会默认生成一个构造函数。
      • 如果类成员变量有引用或者const变量, 编译器无法生成合法默认构造函数, 所以编译失败。
    2. 默认的拷贝构造函数:
      • 当用户不定义拷贝构造函数时,编译器可以尝试生成默认的拷贝构造函数。
      • 只有当用户使用到拷贝构造函数时,编译器才会尝试去生成。
    3. 默认拷贝赋值运算符:
      • 当用户不定义拷贝构造函数时,编译器会尝试生成默认赋值运算符。
      • 同样的,一些情况下也无法默认生成,例如:类成员变量有引用或者const变量。
    4. 默认的析构函数

    最后,总结一下,编译器默认生成的四种函数,有以下共同点:

    1. 只有当用户使用到拷贝构造函数时,编译器才会尝试去生成;
    2. 如果基类中对应的构造/拷贝构造/赋值运算符是私有的,编译器也无法生成。
    3. 默认生成的函数都是inline的, 并且是public的。

    若不想使用编译器自动生成的函数,明确拒绝。

    1. 只声明想要禁止的函数,不定义,并且声明为private的。
    2. c++11之后, 引用了delete关键字,使用delete关键字修改。delete关键字可以修饰任何成员函数的。
    3. 定义一个基类,并且把基类的相应函数声明为delete或者private, 然后子类继承它,这样的的话,子类的相应函数也是无法使用的。

    为多态的基类声明virtual 析构函数。

    1. 如果一个类想作为基类,并且想使用引用或指针用多态,该应该把析构函数声明为virtual的。
    2. 如果一个类不想作为基类,或者不想用于多态用途,则没有必要声明为virtual的构造函数。
    3. 一个析构函数可以声明为纯虚函数,但是必须要定义它才行。否则,出现链接错误,子类的析构函数无法调用到基类的析构函数。
    4. 析构函数的运作方式:最深层的子类的构造函数先执行,然后一层层地向外调用对应基类的析构函数。如果通过基类的指针调用析构函数,并且析构函数不是虚函数,则只会调用基类的虚函数,不会调用子类的虚函数。

    绝不在构造和析构函数中调用virtual函数。

    1. 在构造和构造函数中调用虚函数时,不会执行多态操作。
    2. 在子类对象的base class 构造期间, 对象的类型不是子类,而是基类。
    3. 同样的,在子类对象的base 析构期间,子类已经先被析构掉了,此时的对象类型是基类。
    4. 额外补充一点:虚函数的默认参数值是静态绑定的,不是动态绑定的。所以在多态期间,子类里面的虚函数的默认参数其实是基类的默认参数。

    operator=操作符

    1. 它必须是类成员函数。
    2. 重载时,它的返回值应该是this指针的引用
    3. 处理自我赋值的情况:使用技巧 copy and swap 技术。

    实现

    尽力少做转型的动作

    1. c++中的四种类型转换
      • const_cast, 移除对象的const属性。
      • dynamic_cast, 主要用于来执行“安全向下”的类型转换, 它可能会耗费重大的运行成本。 它的许多实现版本执行速度相当慢,例如一个很普遍的实现版本是基于"class 名称之字符串比较". 如果在四层深的单继承体系中的对象是执行dynamic_cast, 可能最多可达四次的strcmp调用,深度继承和多继承的成本更高。
      • reinterpret_cast, 意图执行低级转换,实际动作可能取决于编译器,具有不可移植性。
      • static_cast, 用于强迫隐式转换。
    2. 许多程序员认为转型其实什么也不做,这个是错误的。
      • 一个类型转换也往往真的令编译器编译出运行期间的执行代码,例如:把一个int 转换为double,几乎肯定会产生一些代码,int 与double的底层实现不同。
      • 在c++的多继承中,基类指针与子类指针往往是不相同的,这种情况下会有一个偏移量在运行期被施行于子类指针身上。

    透彻了解inline的里里外外

    1. 使用inline函数可以避免函数调用带来的额外开销。
    2. 编译器的优化机制通常被设计用于浓缩那些“不含函数调用“的代码,使用inline函数之后,编译器可以进行一部分的优化功能。
    3. 当inline函数体过大时,如果内联函数调用点过多的话,可能会引起目标文件变大,进而影响到 i cache的命中率。
    4. 当inline的函数体较小时,可能会导致目标码较小,以及较高的 i cache命中率。
    5. virtual 函数不会变成内联函数,原因很简单:virtual意味着等到运行期才确定调用哪个函数,而内联是在编译期决定的。
    6. 使用函数指针对内联函数的调用,编译器通常不会实施inlinling.
    7. 大部分的调试器对于inline函数往往束手无策,所以开发过程中调试很麻烦的。

    继承与面向对象

    避免遮掩继承而来的名称

    1. 先说一下子类与基数的作用域。当子类查找一个函数时,会先在子类空间中查找,如果找不到,再去基类空间去继续,如果还查不到,再去全局作用域空间中去查找。
    2. 再说一下重载,我理解的重载只有发现在同一层作用域中,子类的同名函数不可以与基类的同名函数之间发生重载,如果形参不匹配,编译直接报错的。
    3. 对于函数名字的遮掩与重载,与是否为虚函数无关的。函数有的特性,虚函数都有, 虚函数只有当通过指针或引用调用时,才会体现它的多态性,就运行时绑定。通过类对象调用虚函数时也是静态绑定的,编译时决定了。
    4. 如果子类遮掩了继承而来的基类的函数名字,那么无法隐式调用它们了,只能通过显示调用了,Base::Func();
    5. 可以在子类中,使用using 把基类的函数名引入到子类作用域中, 例如:using base::func;

    绝不重新定义继承而来的虚函数的默认值

    1. 通过引用或指针调用虚函数执行时,是进行动态绑定的。而虚函数的默认值是静态绑定的.
    2. 为什么 c++中进行静态绑定函数的默认参数呢? 答案就是因为效率。如果缺省参数值是动态绑定的,编译器就必须有某种方法在运行期为virtual函数决定乱写的参数值,但是这样很复杂并且慢。

    子类与基类中的虚函数的public与private属性不一致会怎么样?

    1. 首先,编译不会报错,允许基类的virtual函数是public, 子类的virtual函数为private,也允许基类的virtual函数为private, 子类的virtual函数为public.
    2. 虚函数为public还是private, 只会影响到静态绑定。
    3. 当基类中的函数为private而子类中为public时,你无法通过基类指针或引用调用该虚函数,但是可以通过子类对象调用。 当基类的虚函数为public而子类中为private时,可以通过基类的指针或引用该用该虚函数,但是不可以通过子类调用虚函数。

    当子类与基类之间是protected或者private继承时,会怎么样?

    1. 无法完成由子类到基类的转换,因为基类是无法访问的。

    多重继承

    1. 同一层级的基类之间的函数,不可以进行重载的。
    2. 当发生棱形继承时,如果共同的基类不是虚继承,在子类中会生产共同基类的复制形为。虚继承是需要代价的。

    定制new 与delete

    我之前的笔记,已经总结的非常好了。https://www.cnblogs.com/yinheyi/p/12900305.html

  • 相关阅读:
    iOS 手势操作:拖动、捏合、旋转、点按、长按、轻扫、自定义
    一个基于MVVM的TableView组件化实现方案
    代码审查和不良编程习惯
    十二步创建你的第一个JavaScript库
    可简单避免的三个 JavaScript 发布错误
    巧用Javascript将相对路径地址转换为绝对路径
    jquery 事件对象属性小结
    26个Jquery使用小技巧
    应用于网站导航中的 12 个 jQuery 插件
    使用 jQuery 避免鼠标双击
  • 原文地址:https://www.cnblogs.com/yinheyi/p/14772827.html
Copyright © 2020-2023  润新知