【问题标题】:Do uninitialized objects exist in C++?C++ 中是否存在未初始化的对象?
【发布时间】:2017-09-26 13:19:34
【问题描述】:

如果所有对象都至少有一个构造函数,无论是编译器定义的默认构造函数还是用户定义的默认构造函数,那么如何才能取消初始化对象。

【问题讨论】:

  • 并非所有对象都有构造函数,例如int
  • C++ 在某些情况下,例如自动存储int n;,有一个“默认初始化意味着不执行初始化”的有趣概念。所以它被初始化但它没有被初始化。
  • 您可以将int 放在class 中,除非您对其进行初始化,否则它将保持未初始化状态。
  • IMO 更容易将 all 对象类型视为具有 c'tors。只是对于某些人来说,那些 c'tors 是无操作的。因此是不确定的值。
  • @rimiro 标准中的措辞有些混乱。当您编写int i; 时,i默认初始化的。但是intdefault-initialization 的定义是“不执行初始化”。通俗的说,我们只是称它为未初始化。

标签: c++ initialization


【解决方案1】:

可以声明不进行初始化的对象。这些对象确实存在,它们有一个不确定的值,并且使用这个值是未定义的行为(这个规则对于字符有一个例外)。

这样的对象可以通过默认初始化来创建。 这在 c++ 标准中有所说明,(§11.6 Initializers)[dlc.init]:

默认初始化一个 T 类型的对象意味着:

(7.1) — 如果 T 是(可能是 cv 限定的)类类型(第 12 条),则考虑构造函数。适用的 枚举构造函数(16.3.1.3),并通过重载选择 initializer () 的最佳构造函数 决议(16.3)。如此选择的构造函数被调用,带有一个空参数列表,以初始化 对象。

(7.2) — 如果 T 是数组类型,则每个元素都是默认初始化的。

(7.3) — 否则,不执行初始化。

尽管如此,静态对象总是零初始化。因此,任何具有动态或自动存储持续时间的内置对象都可能不会被初始化,即使它是一个子对象;

int i; //zero-initialized

struct A{
  int i;
  };

struct B
  {
  B(){};
   B(int i)
    :i{i}{}
  int i;
  int j;
  };
A a; //a.i is zero-initialized

int main()
  {
   int j;             //not initialized
   int k{};           //zero-initialized
   A b;               //b.i not initialized
   int* p = new int;  //*p not initialized
   A*   q = new A;    //q->i not initialized
   B ab;              //ab.i and ab.j not initialized
   B ab2{1};          //ab.j not initialized
   int xx[10];        //xx's element not initialized.

   int l = i;    //OK l==0;
   int m = j;    //undefined behavior (because j is not initialized)
   int n = b.i;  //undefined behavior 
   int o = *p; //undefined behavior 
   int w = q->i; //undefined behavior 
   int ex = x[0] //undefined behavior
   }

对于成员初始化 [class.base.init] 可能会有所帮助:

在非委托构造函数中,如果给定的潜在构造子对象不是由 mem- initializer-id (包括没有 mem-initializer-list 的情况,因为构造函数没有 ctor-initializer),然后 — 如果实体是具有默认成员初始值设定项 (12.2) 的非静态数据成员,并且

(9.1.1) — 构造函数的类是联合 (12.3),并且没有指定该联合的其他变体成员 通过 mem-initializer-id 或

(9.1.2) — 构造函数的类不是联合,如果实体是匿名联合的成员,则没有 该联盟的其他成员由 mem-initializer-id 指定, 实体是从 11.6 中指定的默认成员初始化程序初始化的;

(9.2) — 否则,如果实体是匿名联合或变体成员 (12.3.1),则不进行初始化 执行;

(9.3) — 否则,实体默认初始化 (11.6)

普通匿名联合的成员也可能不会被初始化。


还可以询问对象生命周期是否可以在没有任何初始化的情况下开始,例如使用 reinterpret_cast。答案是reinterpret_cast creating a trivially default-constructible object

【讨论】:

  • 所以int x; 没有初始化但是int x[10]; 是零初始化的?好像不对,是不是你漏掉了另一个子句?
  • 如果我没记错的话,这些都记录在 Stroustrup 的书中!我支持你的情况。
  • @MarkRansom 数组没有什么特别之处,不执行初始化。您可以在此处检查程序集:godbolt.org/g/7TJWXg
  • 对不起,我没有意识到 7.2 是一个 recursive 定义。要么是那个,要么是我严重误读了它。
  • 什么,没有展示位置new::new( (void*)&a ) A;a.i 不再初始化。
【解决方案2】:

该标准没有谈论对象的存在,但是,有一个对象生命周期的概念。

具体来说,来自[basic.life]

T 类型对象的生命周期开始于:

  • 获得与T类型正确对齐和大小的存储,并且

  • 如果对象有非空初始化,则其初始化完成

非空初始化定义为

如果一个对象属于一个类或aggregate type,并且它或其子对象之一由trivial default constructor以外的构造函数初始化,则称该对象具有非空初始化。

我们可以得出结论,对于具有空初始化的对象(例如ints),它们的生命周期在它们的存储被获取时就开始了,即使它们没有被初始化。

void foo()
{
    int i;  // i's lifetime begins after this line, but i is uninitialized
    // ...
}

†​​ 添加链接是为了便于阅读,它们不会出现在标准中

【讨论】:

  • int i 据我所知是默认初始化的,并且 int 的默认 c'tor 不会初始化它(或者 i 被初始化为不确定的值)。如果没有默认 c'tor,它将被称为未初始化(毫无疑问),但如果默认 c'tor 存在,那么它可以以任何它想要的方式初始化对象,甚至可以认为它选择取消初始化对象作为它的某种形式的初始化,为什么它应该被认为是未初始化的? //我只是一个初学者,如果我在任何地方都错了,请纠正我
  • @rimiro 'uninitialized' 是指对象的状态,而不是构造函数是否运行的天气。因此,如果默认 c'tor 什么都不做,则该对象保持未初始化状态并且其值不确定。
猜你喜欢
  • 2010-12-06
  • 2017-01-24
  • 1970-01-01
  • 1970-01-01
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
相关资源
最近更新 更多