zoukankan      html  css  js  c++  java
  • 详细解读《Effective C++》条款04 :确定对象被使用前先被初始化

    1、为什么要确定对象被使用前先被初始化?

    在不同的语境中,同一个对象,可能被编译器给默认初始化,可能编译器没有执行默认初始化。使用没有被初始化的对象就可能导致“不确定行为”。

    我们虽然有关于什么时候对象被编译器初始化,什么时候不被编译器初始化的规则,但是这些规则比较复杂,记忆起来比较困难。

    这些规则总结起来就是:c part of c++ 部分,如果初始化的话可能招致运行期成本,因此不保证初始化。而non-C parts of C++部分一般会保证对象被初始化。但是规则不是一层不变的,总之规则是比较复杂的。

    我们既不想记忆这些规则,又不想导致“不确定行为”,那么处理这件事最好的方法就是:永远在使用对象之前将它初始化。

    2、两类变量的初始化

    (1)(没有成员的)内置类型
    必须手动进行初始化。
    (2)内置类型以外的类型(类类型)
    使用构造函数进行初始化。

    3、搞清楚类类型的初始化与赋值的区别

    C++规定,成员变量的初始化动作发生在进入构造函数之前。在构造函数中的进行的是赋值操作而不是初始化操作。

    4、类类型应该使用初始化列表初始化的原因

    如果没有使用列表初始化进行对类类型成员初始化,而是在构造函数内部对类类型成员进行初始化的话,它其实等价于先进行默认初始化,然后在对类类型成员进行copy赋值。而默认初始化的工作相当于是白做的。而使用初始值列表初始化的话,相当于直接copy初始化。不出先使用默认构造函数初始化。这就是其效率差别的原因。

    5、内置类型应该使用初始化列表初始化的原因

    对于类类型而言,使用由于上述差别,出于效率考虑,我们一般应该使用初始化列表对类类型的成员进行初始化。对于内置类型来说,在效率上,使用初始值列表初始化或者在函数体内copy赋值之间没有差别。但是有时即使将他们进行初始值列表初始化,并不是基于效率的考虑,而是基于正确性的考虑。当成员变量是const的内置类型,或者为引用类型的话,那么必须在定义时初始化,否则就是错误的。

    综合上述,我们最好使用初始值列表对无论是类类型还是内置类型进行初始化,而不是在函数体内copy赋值。

    6、上述规则的例外

    有些时候一个类拥有许多个构造函数,而且拥有很多个基类。这时,使用初始化列表对他们进行初始化的话,对程序员来说,就显得很啰嗦无聊。这时可以遗漏在效率上和初始值列表进行初始化一样的的成员变量,改用copy赋值。
    例如:成员变量的初始值列表由文件或者数据库读入时,这种做法比较有用。

    7、和初始化相关的另外一个小问题:初始化顺序

    C++的成员初始化顺序非常固定:

    • 派生类多包涵的基类对象先初始化
    • 派生类对象所特有的成员变量按照定义顺序初始化

    在这里需要注意一个问题,初始化顺序与初始值列表中的顺序与无关,只与类中变量定义的顺序相关。因此,为了避免产问题,我们最好让初始值列表顺序和类中定义的变量顺序一致。

    8、关于初始化的又一个问题:不同编译单元内定义“non-local static对象”的初始化顺序为题

    static对象是指定义于namespace作用域内的对象、在class内、函数内、以及在life作用域内被声明为static的对象。而函数内的static对象被称为local static对象,其它的就被称为non-local static对象。我们要讨论的就是这些对象的初始化问题。

    编译单元指的是:单一的目标文件的源码,加上所包含的头文件的源码,所构成的目标文件。

    现在的问题就出在:C++对定义在不同编译单元内的non-local static 对象的初始化顺序并无明确定义。

    这样的话,假如文件1中某个non-local static对象y要使用文件2中某个non-local static对象x进行初始化,而编译器并不能x在y之前被初始化,这样就会导致问题出现。

    解决这个问题的方法Singleton设计模式
    具体做法:
    将每个non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference指向它所含的对象。然后用户调用这些函数,而不直接指涉这些对象。换句话说,non-local static 对象,被local static对象替换了。因为C++保证,函数内的local static对象会在“该函数调用期间”“首次遇上该对象之定义式”时被初始化。

    所以函数调用(返回一个reference 指向 local static对象)替换直接访问"non-local static 对象",就保证了你所获得的reference指向了一个初始化后的对象。更好的是,如果没有调用这样的函数,就不会引发构造和析构函数,这是使用non-local static 对象这样的对象所不具备的。

    保证这种方法成功的一个前提是:两个对象之间有着合理的初始化顺序。如果A依赖B,而反过来B又依赖于A。那么这种方法就不成立了。

    9、这种特殊函数在多线程系统中产生不确定性的解决方法

    任何一种non-static对象,无论是否为local或non-local,在多线程系统中等待某件事发生都会遇到麻烦。

    解决这种麻烦的方法:在程序的单线程启动阶段手工调用这样的函数,可消除与初始化有关的竞速形式。

  • 相关阅读:
    【Spring源码这样读】-再次走近容器Spring IOC 一
    【Spring源码这样读】-下载安装一份Spring源码
    【Spring源码这样读】-认识Spring的基本功能
    【Spring源码这样读】-怎么阅读源码
    RabbitMQ没有延时队列?学会这一招玩转延时队列
    【HDU 3746 Cyclic Nacklace】
    10要点解决IE6兼容性问题
    《遍地风流》- 阿城
    PyCharm2021使用教程 --- 1、PyCharm的下载与安装
    爬虫系列 | 6、详解爬虫中BeautifulSoup4的用法
  • 原文地址:https://www.cnblogs.com/lasnitch/p/12764246.html
Copyright © 2011-2022 走看看