【问题标题】:3 types of Initializations [duplicate]3种类型的初始化[重复]
【发布时间】:2012-02-18 16:12:38
【问题描述】:

可能重复:
What do the following phrases mean in C++: zero-, default- and value-initialization?

今天我了解了 C++ 中的 3 种初始化类型:

  • 零初始化
  • 默认初始化
  • 值初始化

我用谷歌搜索过,但没有找到满意的结果。我得到的只是一些标准。到目前为止我所理解的是:在值初始化的情况下,数据成员在某些情况下可以获得等于零的值。

请通过示例详细说明它们(标准)。另外请不要只提供标准中的文字。

谢谢

【问题讨论】:

    标签: c++ initialization value-initialization


    【解决方案1】:

    初始化类型参考语言语法。这里有两个例子:

    T * p1 = new T;
    T * p2 = new T();
    

    对象*p1是默认初始化的,对象*p2是值初始化的。

    初始化的效果取决于T 的类型: 1) 如果T 是一个基本类型,那么默认初始化什么都不做(即对象被留下未初始化),而值在这种情况下,初始化等于零初始化,意味着对象被设置为零。

    2) 如果T 是一个聚合(即没有构造函数、析构函数或赋值运算符的类),则每个元素都递归地进行默认或值初始化。

    3) 如果T 是类类型并且确实有用户定义的构造函数,那么默认初始化和值初始化都会导致调用默认构造函数。

    请注意,具有构造函数的类的成员对象可以依次进行默认或值初始化:

    struct Foo {
        int x;
        int y;
        Foo() : x() { }
    };
    

    现在当您说Foo a; 时,a 是默认初始化的,因此会调用默认构造函数。这反过来又导致a.x 被赋值,即零初始化,而a.y 保持默认值,即未初始化。

    (请注意,实际上不可能对自动对象进行值初始化,尽管在 C++11 中,可以使用大括号初始化来导致值初始化,就像在 Foo a{}; 中一样。(这与Foo a; 在我们的例子中,在第三段之后。))

    【讨论】:

    • 存储类说明符会像在 C 中那样影响初始化吗?具体来说,在 C 中,static 类型是零初始化的(而不是未初始化的)。 C++ 也是这样吗?
    • @cha0site: 是的,8.5/9 [注意:每个静态存储持续时间的对象在程序启动时初始化为零,然后再进行任何其他初始化。在某些情况下,稍后会进行额外的初始化。 --end note ] -- 所指的附加初始化是动态初始化(例如,如果函数的结果存储在该变量中)。
    • @cha0site:确实,正如 Matthieu 所说,具有静态存储的对象会经历静态初始化,包括零初始化和常量初始化。这本质上是一个硬编码的初始值,被编译到程序中。但 OP 的问题与此无关,所以我只需要说明 Foo a; 需要在自动上下文中。
    • @kerrel sb:能否请您在回答中介绍一下大括号初始化?
    • @T.J.:大括号初始化为T a { x, y, z };。它可能意味着很多事情。第 8.5 节是一般参考。
    【解决方案2】:

    这在 8.5 Initializers [dcl.init] 中处理。


    零初始化

    5/ 零初始化T 类型的对象或引用意味着:

    ——如果T是标量类型(3.9),则对象设置为值0(零),作为整数常量表达式,转换为T。如 4.10 中所述,将值为 0 的整型常量表达式转换为指针类型会导致空指针 价值。

    —如果T 是(可能是 cv 限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都初始化为零,填充初始化为零位;

    —如果T 是(可能是 cv 限定的)联合类型,则对象的第一个非静态命名数据成员被零初始化,填充被初始化为零位;

    ——如果T是一个数组类型,每个元素都是零初始化的;

    ——如果T是引用类型,则不执行初始化。

    基本上,它等价于memset(&obj, 0, sizeof(objt));,只是它说明空指针的内存表示可能不是 0 值(即使它在语言中用 0 表示)。

    // foo.cpp
    static char const* p;     // p is zero-initialized
                              // during static initialization
    
    static void init() {
      if (!p) { p = new char[500]; }  // fine as p has been 0-initialized
    }
    

    注意:我个人还是更喜欢用= nullptr来初始化p,只是为了说明意图……


    默认初始化

    6/ 默认初始化T 类型的对象意味着:

    ——如果T 是一个(可能是cv 限定的)类类型(第9 条),则调用T 的默认构造函数(如果T 没有可访问的默认构造函数,则初始化是错误的) ;

    ——如果T是一个数组类型,每个元素都是默认初始化的;

    ——否则,不执行初始化。

    如果程序调用 const 限定类型 T 的对象的默认初始化,T 应是具有用户提供的默认构造函数的类类型。

    或者基本上,调用默认构造函数,考虑数组,至少对于类。最后一点是对内置函数的警告(例如int)。那些只是保持原样(里面有垃圾)。

    默认初始化是当你定义一个变量但没有显式初始化它时调用的。初始化列表中未列出的类的属性也会发生这种情况。因此,内置函数的警告对程序员来说非常重要。

    int function() {
      int a;         // <-- a is default-initialized (which means nothing happens...)
      return a;      // <-- uses a, so technically undefined behavior
    }
    
    struct A { int a; A() {} }; // During the call to A::A(),
                                // A::a is default-initialized (nothing happens...)
    

    没有显式初始化是 C 语言的遗留问题。通常出于优化原因,但如果尝试使用该值会导致未定义行为...


    值初始化

    7/ 对T 类型的对象进行值初始化意味着:

    — 如果T 是(可能是 cv 限定的)类类型(第 9 条),带有用户提供的构造函数 (12.1),则调用 T 的默认构造函数(并且初始化格式错误如果T 没有可访问的默认构造函数);

    ——如果T 是一个(可能是 cv 限定的)非联合类类型,没有用户提供的构造函数,那么对象是零初始化的,如果 T 的隐式声明的默认构造函数是非-trivial,调用该构造函数。

    ——如果T是一个数组类型,那么每个元素都是值初始化的;

    ——否则,对象被零初始化。

    值初始化的对象被认为是已构造的,因此受本国际标准适用于“构造的”对象、“构造函数已完成的对象”等的规定的约束,即使没有调用构造函数用于对象的初始化。

    它是上述两者的混合,意味着以下语法:

    template <typename T> T value() { return T(); }
                                             ^~~
    

    提供T 的适当初始化实例,无论T 是类类型还是内置类型。模板化代码能够拥有如此统一的语法非常重要。

    请注意,在 C++11 中,也可以使用 T{} 来实现相同的效果(这有助于消除函数的歧义)。

    【讨论】:

    • :您能否在您的回答中说明大括号初始化
    猜你喜欢
    • 2017-12-30
    • 2013-02-10
    • 2017-01-30
    • 2018-08-09
    • 2011-09-25
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多