【问题标题】:Default initialization of class data members in C++11C++11中类数据成员的默认初始化
【发布时间】:2016-03-27 04:23:58
【问题描述】:

我对类数据成员的默认初始化感到困惑。这是示例代码。

#include <iostream>
#include <vector>

class A {
public:
  int i;
  A() {}
};

A a1;
A aa1[3];
std::vector<A> av1(3);

int main()
{
  A a2;
  A aa2[3];
  std::vector<A> av2(3);
  std::cout << a1.i << " " << a2.i << std::endl;          // 0         undefined
  std::cout << aa1[0].i << " " << aa2[0].i << std::endl;  // 0         undefined
  std::cout << av1[0].i << " " << av2[0].i << std::endl;  // undefined undefined
}

在上面的代码中,只有a1.iaa1[0~2].i被初始化为0,其他的都没有初始化。我不知道为什么会这样。

具体来说,我已经知道的是(来自“C++ Primer”):

  • 初始化的过程是:

    • a1a2默认初始化
    • aa1aa2 的每个元素都默认初始化
    • av1av2 的每个元素都被值初始化
  • 默认初始化的过程是:

    • 检查变量是内置类型还是类类型
    • 对于内置类型,如果变量在任何函数体之外,则初始化为0,否则为未定义值。
    • 对于类类型,如果类有默认的ctor,则调用,否则为编译错误。
  • 值初始化的过程是:

    • 检查变量是内置类型还是类类型
    • 对于内置类型,初始化为0。
    • 对于类类型,它是默认初始化的。 (我认为这意味着如果该类具有默认 ctor,则调用它,否则它是编译错误。)

那么当调用ctorA::A()时,数据成员A::i是如何初始化的(我猜是默认初始化的)?还有为什么只有a1.iaa1[0~2].i被初始化为0,而其他的都未初始化?

【问题讨论】:

  • 您不能从输出 0 得出变量已“初始化为 0”的结论。它可能未初始化并且该内存位置恰好包含0。更重要的是,输出未初始化的变量会导致整个程序具有未定义的行为,因此您无法从输出中真正得出任何可靠的结论。

标签: c++ c++11


【解决方案1】:

你为A声明了一个构造函数,所以i没有默认初始化;这是您在构造函数中的责任。全局变量的初始值为 0,因为它们是全局变量。如果没有通过某种方式赋予它们初始值,则所有全局变量都将初始化为 0。

局部变量和向量获取随机数据,因为这是那些 A 实例正在使用的堆栈(用于局部变量)或堆(用于向量使用的已分配内存)上的内存中的内容。

【讨论】:

  • 感谢解释,如果我省略了构造函数并使用编译器生成的默认构造函数,A::i 会被初始化为 0 吗?
  • A::i 只有在构造函数中才会被初始化为零,否则它总是未初始化。
  • @legends2k 并不是那么简单:向量元素是由new(location) A() 构造的。如果A 有一个不初始化i 的用户定义构造函数,那么这意味着i 将未初始化;但否则A 是一个聚合,因此i 将设置为0
  • @M.M 如果 A 是一个聚合,你的意思是我们有一个聚合向量?然后,是的,聚合的所有元素都将被初始化。我觉得聚合初始化是一个极端情况,不想将 OP 与它混淆。
【解决方案2】:

如果全局(静态和导出)变量没有被显式初始化并且没有构造函数。

当您使用您在代码中使用的构造函数时,vector 的项是默认初始化的。

对于向量类对象,两者都意味着构造函数调用。但是对于整数,第一个意味着初始化为零,而后者意味着初始化为不确定的值。所以代码中的普通 int 变量是零初始化的,但向量中的 int 是默认初始化的。

一些参考链接:

【讨论】:

    【解决方案3】:

    当调用ctor A::A()时,数据成员A::i是如何初始化的

    如果没有提供初始化器,则应用默认初始化规则。你的构造函数没有初始化A::i,所以它没有初始化;它的价值是不确定的。毫无疑问。摘自documentation on default initialization

    如果 T 是类类型,则考虑构造函数并对其空参数列表进行重载决策。调用选择的构造函数(默认构造函数之一)为新对象提供初始值。


    为什么只有 a1.i 和 aa1[0~2].i 初始化为 0,而其他都未初始化?

    全局数据存储器被初始化为零,即整个部分被清零,因此您看到全局A::is 初始化为0。请注意,构造函数不会这样做。摘自documentation

    静态初始化

    [...]

    2) 对于所有其他非本地静态和线程本地变量,Zero initialization 发生。在实践中,将要进行零初始化的变量放在程序映像的 .bss 段中,该段不占用磁盘空间,并在加载程序时被操作系统清零。

    但是,对于vectorvector 本身位于非本地静态内存中,而其元素分配在空闲存储(堆)中,因此它们的成员也未初始化。

    【讨论】:

    • 感谢您的回答,现在很清楚了 :) 顺便说一句,当我阅读有关 storage duration 的 wiki 时,在一些旁边有一个标记 (直到 C++11)段落,是否意味着这些段落在 C++11 之前有效并包含 C++11,但在 C++11 之后将被弃用?
    • 这意味着 para 一直保持到 C++03,C++11 之前的标准,而不是 C++11 及更高版本(如 C++17 等)。
    【解决方案4】:

    类类型对象的初始化是由类控制的,这个过程递归地完成。以您的示例代码为例,i 的初始化由A 控制。由于没有提供构造函数,因此将使用合成的默认构造函数。并且由于没有提供类内初始化器,A 的每个成员都被默认初始化,再次查询默认初始化规则。内置类型i 将保持未初始化状态。如果A 包含s 类型的string 成员,则s 的默认初始化由string 类控制(递归规则发生)。在这种情况下,s 将被初始化为一个空字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-10
      • 1970-01-01
      • 1970-01-01
      • 2020-02-08
      • 2015-03-22
      相关资源
      最近更新 更多