3种初始化类型

3 types of Initializations

本文关键字:类型 初始化 3种      更新时间:2023-10-16

可能重复:
以下短语在C++中是什么意思:零初始化、默认初始化和值初始化?

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

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

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

请举例说明它们(标准)。此外,请不要只提供标准中的文本。

感谢

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

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;的行为完全相同,这是第三段的结果。)

这在8.5初始化程序[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是数组类型,则默认初始化每个元素;

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

如果程序调用常量限定类型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是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9条),则调用T的默认构造函数(并且如果T没有可访问的默认构造函数,则初始化不正确);

--如果T是一个(可能是cv限定的)非并集类类型,没有用户提供的构造函数,则对象为零初始化,如果T隐式声明的默认构造函数是非平凡的,则调用该构造函数。

--如果T是数组类型,则每个元素都被值初始化;

--否则,对象初始化为零。

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

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

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

无论CCD_ 39是类类型还是内置类型,都提供了CCD_。模板化代码能够拥有这样一个统一的语法是很重要的。

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