【问题标题】:Are members of structs in a vector initialized with zero in C++?向量中的结构成员是否在 C++ 中初始化为零?
【发布时间】:2014-10-01 15:11:13
【问题描述】:

在 C++ 中,当我有类似的结构时

struct myStruct
{
  int i;
  bool b;
  MyClass *myobj;
};

然后我制作一个向量

std::vector<myStruct> myVector;

然后我调整向量的大小

myVector.resize(10);

结构的成员会用零(包括指针)初始化吗?我不能假设结构的成员中可能存在随机数据吗?

【问题讨论】:

  • 请注意,在这种情况下,类和结构之间没有区别,就像在其他语言中一样,例如C#。

标签: c++ vector struct


【解决方案1】:

在这种特殊情况下,由于std::vector::resize() 的属性,答案是YES

If the current size is less than count, additional elements are appended and initialized with copies of value.      (until C++11)

If the current size is less than count,  (since C++11)
    1) additional value-initialized elements are appended
    2) additional copies of value are appended

对于正常情况,答案将是 NO,例如myStruct s,这将使他们 (s.i, s.bool, s.myobj) 留下不确定的值

如果您仍然希望它们按预期初始化,请为结构创建构造函数:

struct myStruct {
    int i;
    bool b;
    MyClass *myobj;

    myStruct():i(0),b(false),myobj(NULL) { }  // <- set them here by default
};

【讨论】:

  • 那个 cppreference 引用过于简单化了。实际的初始化语义取决于分配器的construct() 的定义。此外,未初始化的对象具有 indeterminate 值,而不是未指定的值。
【解决方案2】:

我实际上不敢苟同。 在这种特殊情况下,该标准似乎保证对象将被零初始化。 (以下所有标准参考均为 N3936。)

vector::resize 在这种情况下被指定附加 10 个“默认插入的元素到序列”(§23.3.6.3 [vector.capacity]/p12)。

默认插入又被定义为 (§23.2.1 [container.requirements.general]/p14;X 是容器的类型;mA 类型的左值,这是容器的allocator_type):

X 的元素如果通过求值初始化,则默认插入 表达方式

allocator_traits<A>::construct(m, p)

其中pX 中分配的元素的未初始化存储地址。

由于编写的代码使用默认分配器,allocator_traits::construct 调用只是在std::allocator&lt;myStruct&gt; 的实例上调用construct(p)(§20.7.8.2 [allocator.traits.members]/p5),它被指定为(§20.7.9.1 [allocator.members]/p12)

template <class U, class... Args>
void construct(U* p, Args&&... args);

12 效果::new((void *)p) U(std::forward&lt;Args&gt;(args)...)

由于construct(p)的参数包为空,所以construct()调用的效果是::new((void *)p) myStruct()

标准规定,new-initializers,就像上面放置 new 表达式中的 (),将被解释为“根据 8.5 的初始化规则进行直接初始化”(§5.3. 4 [expr.new]/p17)。反过来,第 8.5 节 [dcl.init]/p11 指定“初始化器为空括号集的对象,即 (),应进行值初始化。”

值初始化被指定为 (§8.5 [dcl.init]/p8)

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

  • 如果T 是(可能是 cv 限定的)类类型(第 9 条),没有默认构造函数 (12.1) 或默认构造函数是 用户提供或删除,则对象默认初始化;
  • 如果T 是一个(可能是 cv 限定的)类类型,没有用户提供或删除的默认值 构造函数,则对象被零初始化,语义约束为 检查默认初始化,如果 T 有一个非平凡的默认值 构造函数,对象是默认初始化的;
  • 如果T是数组类型,那么每个元素都是值初始化的;
  • 否则,对象被零初始化。

在这种情况下,myStruct 是“没有用户提供或删除的默认构造函数的类类型”,这意味着它匹配执行零初始化的第二个要点。因此,myStruct 类型的对象的值初始化意味着该对象将被零初始化。

但是,请注意这里的规则非常复杂,自动零初始化的路径非常脆弱。例如,如果你给myStruct 一个像myStruct() { } 这样的默认构造函数,那么这是一个用户提供的默认构造函数,这意味着它将匹配值初始化的第一个项目符号而不是第二个,这反过来意味着它不会被零初始化。此外,如果您的 vector 使用自定义分配器,这也可能不起作用,因为它的 construct() 可能与默认分配器的语义不同。

因此,最好给myStruct 一个默认构造函数,将其成员显式归零。

【讨论】:

    【解决方案3】:

    是的,在这种特殊情况下,结构将被初始化为零。原因是STL中resize()方法的实现。新元素是默认构造的:

    void resize(size_type __new_size) {
        resize(__new_size, value_type());
    }
    

    value_type() 导致零初始化。

    测试证据

    #include <cstdio>
    
    struct foo {
      int i;
      int j;
      void *k;
    };
    
    void test(const foo& fooinst) {
      printf("%d\n",fooinst.i);
    }
    
    main() {
      test(foo());
    }
    

    这是生成的 g++ 反汇编的相关部分。请注意调用前结构成员的显式零初始化。

    test.cpp      ****   test(foo());
      56                            .loc 1 14 0
      57 002d C745F000              movl    $0, -16(%rbp)
      57      000000
      58 0034 C745F400              movl    $0, -12(%rbp)
      58      000000
      59 003b 48C745F8              movq    $0, -8(%rbp)
      59      00000000 
      60 0043 488D45F0              leaq    -16(%rbp), %rax
      61 0047 4889C7                movq    %rax, %rdi
      62 004a E8000000              call    _Z4testRK3foo
    

    【讨论】:

      【解决方案4】:

      vector 中的值使用struct 的默认构造函数(或初始化值)进行初始化,在本例中为myStruct

      struct 没有默认构造函数,因此将使用编译器生成的构造函数。在这种情况下,它会保留 myStruct 的对象未初始化。

      不,一般来说,您不能假设数据将被初始化为零。

      这种特殊情况(.resize())是在存在值初始化并且从 C++03 和 C++11 发生一些变化(其他答案提供更多细节)的情况下选择的。

      最好的建议仍然存在,要做的是添加适当地初始化 struct 的默认构造函数。

      【讨论】:

        【解决方案5】:

        结构的 ctor 被调用。由于您没有指定一个(并且您可以,即使对于结构),所以使用默认的无操作 ctor。默认ctor不清除成员。

        【讨论】:

        • 这个答案对于std::vectors 的初始化行为实际上是不正确的。 (见接受的答案)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-07
        • 2016-10-05
        • 1970-01-01
        • 2017-03-25
        • 1970-01-01
        相关资源
        最近更新 更多