C++中存在未初始化对象吗?

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

8
并非所有的对象都有构造函数,比如 int - NathanOliver
3
C++有一个有趣的概念,即对于某些情况(如自动存储 int n;),“默认初始化意味着不执行初始化”,因此它被初始化了,但又没有被初始化。 - juanchopanza
3
我认为将所有对象类型视为具有构造函数会更容易理解。只是对于某些对象来说,这些构造函数没有任何操作。因此出现了不确定的值。 - StoryTeller - Unslander Monica
4
标准文件的措辞有些令人困惑。当你写下 int i; 时,i 会被_默认初始化_。但是对于 int 类型的_默认初始化_的定义是_"不执行任何初始化"_。通俗地说,我们只是称之为未被初始化。 - Passer By
2
@Brandon,即使是C语言也将所有内容都称为对象。从术语、定义和符号中可以得知:对象:数据存储区域在执行环境中,其内容可以表示值 - NathanOliver
显示剩余21条评论
3个回答

17
可以声明对象,但不进行初始化。这些对象存在,具有不确定的值,使用此值是未定义的行为(对于chars有一个例外)。
这种对象可以通过默认初始化创建。C++标准(§11.6 Initializers)中指出:

对类型T进行默认初始化的含义如下:

(7.1) — 如果T是(可能带有cv限定符的)类类型(第12节),则考虑构造函数。逐个枚举适用的构造函数(第16.3.1.3节),并通过重载解析(第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指定(包括构造函数没有ctor-initializer-list的情况),则: - 如果该实体是具有默认成员初始化器(12.2)的非静态数据成员,并且 - 构造函数的类是联合(12.3),并且该联合的其他变量成员都没有被mem-initializer-id指定或 - 构造函数的类不是联合,如果该实体是匿名联合的成员,则该联合的其他成员都没有被mem-initializer-id指定, 则该实体将从其默认成员初始化程序中初始化,如11.6所述; - 否则,如果该实体是匿名联合或可变成员(12.3.1),则不执行任何初始化; - 否则,将进行默认初始化(11.6)。
普通匿名联合的成员也可能不会被初始化。
此外,还可以问一个对象的生命周期是否可以在没有任何初始化的情况下开始,例如通过使用reinterpret_cast。答案是“不可以”: reinterpret_cast creating a trivially default-constructible object

那么 int x; 没有初始化,但是 int x[10]; 是零初始化的吗?这似乎不太对,你有没有漏掉其他条款? - Mark Ransom
如果我的记忆没有欺骗我,这些都在Stroustrup的书中有记录!我为你的情况而来。 - Oliv
1
@MarkRansom 对于数组没有什么特别的,不执行初始化。您可以在此处检查汇编:https://godbolt.org/g/7TJWXg - Oliv
抱歉,我没有意识到7.2是一个递归定义。要么我严重地误读了它。 - Mark Ransom
@Yakk 它从未被初始化过!!在这里查看C++98示例中的汇编代码:没有零初始化。奖励:具有用户声明的默认构造函数的类的值初始化会禁用零初始化。因此,如果您不使用{}初始化形式并使用部分初始化非静态成员的类,则最好检查两次您的代码,并将标准放在枕头下! - Oliv
显示剩余2条评论

2
标准并没有讨论对象的存在,但是有一个对象的生命周期的概念。
具体来说,来自于[basic.life] 引用如下:

类型为T的对象的生命周期从以下情况开始:

  • 获得适当对齐和大小以容纳类型T的存储空间;

  • 如果对象具有非平凡初始化,则其初始化完成。

这里“非平凡初始化”被定义为:
引用如下:

如果对象是类或聚合类型,并且它或它的子对象之一由除了平凡默认构造函数之外的构造函数初始化,则称该对象具有非平凡初始化。

我们可以得出结论:对于具有空初始值(例如 int)的对象,它们的“生命周期”从获取存储空间开始,即使它们未被初始化。
void foo()
{
    int i;  // i's lifetime begins after this line, but i is uninitialized
    // ...
}

† 链接仅为方便阅读而添加,它们不会出现在标准中。

据我所知,int i默认初始化,而int的默认构造函数不会对其进行初始化(或者i被初始化为不确定的值)。如果没有默认构造函数,则称之为未初始化(毫无疑问),但是如果存在默认构造函数,则可以以任何方式初始化对象,甚至其选择取消初始化对象也可以看作是某种形式的初始化,那么为什么应该将其视为未初始化??//我只是一个初学者,如果哪里有错误,请指正 - rooni
@rimiro中的“未初始化”是指对象的状态,而不是构造函数是否已运行。因此,如果默认构造函数什么也不做,对象仍然保持未初始化状态,并且其值是不确定的。 - alain

-1
使用字节数组:
alignas(alignof(Mat4)) uint8_t result[sizeof(Mat4)];
// ..
node->updateMatrix( ..., /*result*/ reinterprect_cast<Mat4*>(&result[0])); 

Mat4 的构造函数不会触发。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接