关于c++:初始化-Initialization

58次阅读

共计 6908 个字符,预计需要花费 18 分钟才能阅读完成。

初始化 (Initialization)

  • 初始化的模式

    • Direct-initialization
    • Copy-initialization
  • 初始化的含意
  • Value initialize
  • Default initialize
  • Zero initialize
  • List initialize

    • narrowing conversion
  • 字符数组初始化
  • 聚合(aggregate)初始化

    • Aggregate
  • 援用初始化

基于 c ++20 draft n4868

初始化的模式

通常讲到初始化,会想起变量的时候给的初始化值(initializer)。然而除此之后,还有一些中央会产生对象的初始化。所有的这些初始化操作听从同一套规定。

初始化依照其模式,能够分为两大类,direct-initialization 与 copy-initialization

Direct-initialization

  • 变量定义中初始化值无=

    • int i{1}; std::vector<std::string> vec(3);
  • new-initializer

    • int *p = new int(3);,应用 3 初始化一个新建的 int 对象
  • static_cast

    • static_cast 会应用其参数初始化一个后果类型的对象,如:static_cast<int>(2.0);,其中应用 2.0 初始化一个 int 对象。
  • 函数调用像是的类型转换

    • int(2.0),其中应用 2.0 初始化一个 int 类型的对象
  • condition 中的 brace-init-list

    • if (Derived *pDerived{dynamic_cast<Derived*>(pBase)}),应用 dyanmic_cast<Drived*>(pBase) 初始化 pDerived

Copy-initialization

  • 变量定义中应用 = 模式初始化

    • int i = 1;,应用 1 初始化 i
  • condition 中应用 = 模式初始化

    • if (Derived *pDerived = dynamic_cast<Derived*>(pBase)),应用 dyanmic_cast<Drived*>(pBase) 初始化 pDerived
  • 参数传递,函数返回值

    • 示例:

      int foo(int i) {return i+1;}
      void bar() {foo(1);
      }

      应用 1 初始化 foo 的参数 i,应用 i+1 初始化函数的返回值(右值)

  • 抛出异样,异样捕捉

    • 示例:

      void foo() {std::string s{123};
        try {throw s;} cache (string &es) {}}

      throw s; 中,应用 s 初始化一个长期对象。在 cache (string &es) 中,长期对象的左值用于初始化 es。这两个初始化都是 copy initialize

  • 聚合成员初始化。在聚合初始化中,每个成员的初始化都是 copy initialization

    • std::string str[]{"abc", "def"},应用 "abc" 初始化 str[0],应用 "def" 初始化 str[1] 都是 copy-initialize。留神对 str 的初始化应用的是 direct-initialize 的模式。

初始化的含意

依据被初始化对象的类型与初始化的模式不同,初始化会有不同的含意。具体初始化含意见下。copy-initialize 与 direct-initialize 均应用如下定义。

  • 如果 initializer 是一个 braced-init-list 或者 = branced-init-list,执行列表初始化
  • 如果指标类型是援用类型,则执行援用初始化
  • 如果指标类型是 char, singed char, unsigned char, char8_t, char16_t, char32_t, wchar_t 的数组,initializer 是一个 string-literal,执行字符数组初始化
  • 如果 initializer 是 (),执行 value-initialze
  • 如果指标类型是数组,initializer 应该是 ( pxpression-list ),且其中的元素个数不应比数组长度更多。数组中的元素将顺次由 epression-list 中的值 copy-initialize。对没有提供值的元素(数组长度比 expression-list 长),进行 value-initialize
  • 如果指标类型是类类型(class type):

    • 如果 initializer 是一个右值并且与指标类型雷同(疏忽 cv-qualifier),则 initializer 用于初始化指标对象
    • 如果 1) 这是 direct initialization;或者 2) 这是 copy initialization 且源类型与指标类型雷同,或者是指标类型的派生类,那么应用构造函数初始化

      • 如果能够找到惟一可用的构造函数,那么该构造函数用于初始化对象
      • 如果没有可用的构造函数,且指标类型是一个聚合(aggregate class),intializer 为 ( expression-list ),则应用 expression-list 中的值顺次 copy initialize 对应元素。未初始化值的元素执行 value initialize。
      • 否则,程序非法
    • 否则,尝试用户定义类型转换(包含构造函数)。不能找到适合的类型转换的程序非法。类型转换的后果将被用于 direct initialize 指标对象。
  • 否则,如果源类型是一个类类型,将尝试用户定义类型转换。不能找到适合的类型转换的程序非法。
  • 否则,如果这个是一个 direct initialization,原类型是 std::nullptr_t,指标类型是 bool,则指标对象被初始化为 false
  • 否则,如果须要,能够应用一个规范转换序列 (stand conversion sequence) 将源对象转换为指标类型。无奈转换则程序非法。

Value initialize

对一个类型 value initialize 含意是:

  • 如果其为类类型,则

    • 如果该类没有默认构造函数,或者有用户提供的(user-provided)或 deleted 默认构造函数,该对象被 default initialize
    • 否则,对象被 zero initialize;并且,如果该类有 non-trivial 默认构造函数,该对象被 value initialize。

      • 例如

        #include <string>
        struct A{
          int a;
          std::string s;
        };
        int main()
        {A a_obj{}; // a_obj.a == 0
        }

        A 有默认构造函数(主动生成的),该默认构造函数不是 user-provided,也不是 deleted,因此进入该条解决。
        a_obj 被 zero initialize。另外,因为其默认构造函数是 non-trivial(它含有个一个 std::string 成员),因此被进一步默认结构。
        因为有 zero initialize 存在,a_obj.a == 0

  • 如果指标对象类型为数组,则数组的每一个元素被 value initialize
  • 否则,对象被 zero initialize

Default initialize

对一个类型 default initialize 含意是:

  • 对类类型,调用默认构造函数。
  • 对数组类型,每一个元素被 default initialize
  • 其余,不执行初始化操作

Zero initialize

对一个类型 default initialize 含意是:

  • scalar type 初始化为 0
  • 对非 union 类类型,其 padding 被初始化为 0,非静态数据成员,基类 被 zero initialize
  • 对 union,padding 被初始化为 0,第一个非动态成员被 zero initialize
  • 对数组,每一个元素被 zero initialize
  • 对援用,不进行初始化

List initialize

  • 如果 brace-init-list 蕴含 designated-initializer-list,指标类型必须为聚合(aggregate class),其中成员的呈现程序须要与定义程序统一。执行聚合初始化(aggregate initialize)
  • 如果指标是聚合类(aggregate class),initializer list 仅蕴含一个元素,其类型与指标类型雷同或者是其派生类,那么应用该元素初始化指标对象。
  • 如果指标类型是数组,initializer list 仅蕴含一个元素,且为适合类型的字符串字面量,则执行字符数组初始化。
  • 如果指标类型是聚合类型(aggregate class),执行聚合初始化
  • 如果 initializer list 为空,指标类型为有默认构造函数的类类型,指标对象被 value initialize
  • 如果指标类型是 std::initializer_list<E>:创立一个 const E[N] 类型的数组,其中每一个元素用 initializer list 中的元素 copy initialize,指标对象将援用这个数组。(如果任何一个元素初始化是须要 narrowing conversion,则程序非法)
  • 如果指标类型是类类型,应用构造函数初始化。(如果参数初始化时须要 narrowing conversion,程序非法)
  • 如果指标类型是有确定 underlying type 的枚举,intiiailizer list 仅含有一个元素,该元素能够被隐式转换为指标类型的 underlying type,并且该初始化是 direct initialization,则指标对象的值为源对象转换为指标类型的 underlying type 后的值。(不能够是 copy initialize;如果须要 narrawing conversion,程序非法)
  • 如果 initialize list 有惟一元素,指标类型不为援用或者为一个 reference related to 源类型的援用,指标对象(或援用)由该元素初始化(如果须要 narrowing conversion,程序非法)
  • 如果指标类型是援用,则用 initializer 执行 copy list initialize 初始化一个被援用类型的 prvalue,指标援用由该 prvalue direct initialize
  • 如果 initializer list 为空,指标对象被 value initialize
  • 其余,程序非法

narrowing conversion

narrowing conversion 包含:

  • 从浮点类型到整形的转换
  • long doubledoublefloat,从 doublefloat 的转换,除非源值为常量表达式,且其在指标类型中可示意(包含可示意但无奈准确示意的状况)
  • 从整形或 unscoped enueration type 到浮点类型的转换,除非源值是一个常量表达式,且转换后果在指标类型可示意,且将转换后果再转换为源类型时其值不变
  • 从整形或 unscoped enueration type 到整型的转换,该整型无奈示意源类型中的所有值,除非源值是一个常量表达式,其值在通过 integral promotion 之后在指标类型可示意
  • 从指针或指向成员的指针类型到 bool

字符数组初始化

字符数组能够用字符串字面量初始化。字符数组的类型须要与字符串字面量类型匹配。字符数组的中的元素由字符串字面量中的对应元素进行初始化。

字符串字面量(包含最初的 '\0')不能比字符数组更长。如果字符串字面量较短,多余的数组元素将被 zero initialize。

聚合(aggregate)初始化

Aggregate

满足以下条件的数组或类(union 也是一品种)称作 aggregate:

  • 没有申明构造函数,或继承(注:应用 using)构造函数
  • 没有公有或爱护的非静态数据成员
  • 没有虚函数
  • 没有虚的、公有的或被爱护的基类

aggregate 的成员指:

  • 对于数组,指它的每一个元素
  • 对于类,指按申明程序的每一个间接基类,以及按申明程序的每一个非静态数据成员(如果类中有匿名联结(union),匿名联结自身是 aggregate 的一个成员,但匿名联结的成员不是)

对于显式提供了值的成员,将应用该值进行 copy initialize。如果须要 narrowing conversion,程序非法。对于匿名联结(anonymous union),只能对其中一个成员提供初始化。

对于非 union 的 aggregate 中没有被显式初始化的成员:

  • 如果成员申明了 default member initializer,应用该值初始化
  • 如果该成员不是援用,应用 {} copy initialize
  • 否则,程序非法

当一个 union aggregate 被应用 {} 初始化时:

  • 如果任意成员(variant member)有 default member initializer,应用该值初始化这一成员
  • 否则,union 的第一个成员应用 {} 初始化

aggregate 的成员如果也是 aggregate,子 aggregate 的初始化列表(在某些状况下)能够省略 {, }。空 {} 不能省略。如果省略,那么接下来的 initializer 全副被用于初始化子 aggregate,直到子 aggregate 所有成员都有了 initializer。残余的 initializer 用于初始化父 aggregate 的残余成员。

援用初始化

先要介绍两个概念:

  • 类型 cv1 T1cv2 T2 是 reference related,示意 T1T2 类似(约等于雷同),或 T1T2 的基类。
  • 类型 cv1 T1cv2 T2 是 reference compitable 示意指向 cv2 T2 的指针能够通过一个规范转换序列(standard conversion sequence)转换为指向 cv1 T1 的指针。(约等于 cv1 > cv2,并且 T1T2 雷同,或者是 T2 的(可拜访的且无歧义的)基类)

应用 cv2 T2 的值初始化 cv1 T1 的援用规定如下:

  • 指标是左值援用并且满足以下条件之一:

    • initialize 为左值,cv1 T1cv2 T2 是 reference compitable,援用绑定到该值(或其基类子对象)
    • T2 是类,但 T1T2 并非 reference related,但 T2 能够转换为一个 cv3 T3 的左值,且 cv1 T1cv3 T3 是 reference compitable 的,援用绑定到转换后的 T3 的左值(或其基类子对象)
  • 否则,如果指标为十分量左值援用,或者 volatile 左值援用,程序非法
  • 否则:(以下为绑定至右值,prvalue 会被转换为 glvalue)

    • 如果初始化值为一个右值(非位域),或一个函数,且 cv1 T1cv2 T2 是 reference compitable,援用绑定到该值(或其基类子对象)
    • T2 是类,但 T1T2 并非 reference related,但 T2 能够转换为一个 cv3 T3 的右值(或函数),且 cv1 T1cv3 T3 是 reference compitable 的,援用绑定到转换后的 T3 的 glvalue(或其基类子对象)
  • 否则:

    • 如果 T1T2 是类,且 T1T2 并非 reference related,则尝试通过自定义转换应用初始化值初始化一个 cv1 T1 的对象(如不能初始化相应的对象,则程序非法)。转换的后果将用于初始化指标援用。
    • 其余状况,初始化值将被隐式转换为 cv1 T1。援用绑定值转换的后果。

      • 如果 T1T2 是 reference related,那么 cv1 应大于 cv2,不能用 lvalue 初始化 rvalue reference。

    例如:

    struct Banana { };
    struct Enigma {operator const Banana(); };
    struct Alaska {operator Banana&(); };
    void enigmatic() {
      typedef const Banana ConstBanana;
      Banana &&banana1 = ConstBanana(); // error
      Banana &&banana2 = Enigma(); // error
      Banana &&banana3 = Alaska(); // error}
    const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0
    double&& rrd = 2; // rrd refers to temporary with value 2.0
    const volatile int cvi = 1;
    const int& r2 = cvi; // error: cv-qualifier dropped
    struct A {operator volatile int&(); } a;
    const int& r3 = a; // error: cv-qualifier dropped
    // from result of conversion function
    double d2 = 1.0;
    double&& rrd2 = d2; // error: initializer is lvalue of related type
    struct X {operator int&(); };
    int&& rri2 = X(); // error: result of conversion function is lvalue of related type
    int i3 = 2;
    double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0

正文完
 0