【问题标题】:initialization of structs/classes without constructors in stack vs heap在堆栈与堆中没有构造函数的结构/类的初始化
【发布时间】:2015-05-06 13:05:21
【问题描述】:

我想知道在 C++ 中将没有默认构造函数的结构(或类)清零的规则。

特别是,如果存储在堆栈中(例如,作为局部变量),它们似乎是未初始化的,但如果在堆上分配,它们是零初始化的(使用 GCC 4.9.1 测试)。 是否保证便携

示例程序:

#include <iostream>
#include <map>
using namespace std;

struct X {
    int i, j, k;
    void show() { cout << i << " " << j << " " << k << endl; }
};

int fib(int i) {
    return (i > 1) ? fib(i-1) + fib(i-2) : 1;
}

int main() {
    map<int, X> m;            
    fib(10);                  // fills the stack with cruft
    X x1;                     // local
    X &x2 = m[1];             // heap-allocated within map
    X *x3 = new X();          // explicitly heap-allocated
    x1.show();  // --> outputs whatever was on the heap in those positions
    x2.show();  // --> outputs 0 0 0 
    x3->show(); // --> outputs 0 0 0     
    return 0;
}

已编辑:删除了粗体部分中的“或者我应该只使用构造函数”;因为让我问的是我想知道它是否是有保证的行为 - 我们都同意使用显式构造函数的可读代码更好。

【问题讨论】:

  • 我没有足够的知识来发布一个充实的答案,但new X() 将初始化X,而new X 不会(在本例中X 是聚合)
  • 您应该始终编写构造函数并设置类的内部变量。不管编译器是否已经设置了它们。
  • @Quentin 这两个语句是一样的,但是应该首选第二个(new X
  • @maja 他们不一样,我更喜欢new X()。为什么更喜欢new X
  • @maja 他们不一样。请参阅hereherehere。在这种情况下,X 要么是值初始化(归零),要么什么都不做(平凡的默认构造函数)。无论如何,初始化规则是一场噩梦,所以只需使用显式构造函数并完成它......

标签: c++ struct initialization


【解决方案1】:

struct 成员进行零初始化的不是动态分配;这是你的语法:

X* ptr = new X();
//            ^^
// 
// as opposed to just:
X* ptr = new X;

如果您希望得到保证,请继续写下去。 :)

另一种与自动存储持续时间等效的替代方法是使用较新的{} 语法:

X* ptr = new X{};   // dynamic
X  obj{};           // automatic

不管怎样,具有静态存储持续时间的对象总是预零初始化,所以默认情况下你会被覆盖。

【讨论】:

  • 不错的答案;我从来没有想过在没有括号的情况下调用 new 进行比较。关于为什么 std::map 是 () 初始化我的结构的任何想法?
  • @tucuxi:因为它就是这样做的! "[C++11: 23.4.4.3/1]: 效果: 如果映射中没有与 x 等效的键,则将 value_type(x, T()) 插入映射中。"标准本可以 已经竭尽全力避免这种值初始化并使用默认初始化,但它会增加复杂性并可能降低有用性。我的意思是,在大多数情况下,拥有一个安全可读的值比避免一次可能冗余的写入更有用。
  • @tucuxi:因为不这样做是愚蠢的。你最终会得到一堆毫无价值的值,无论如何你都必须在之后设置。
  • @tucuxi 我找不到关于此 [在此处插入标准大师调用] 的确定词,但 IINM 仅引用未初始化的变量是 UB,std::map 必须返回这样的引用.
  • @Quentin:不能说我听说过,而且我在 [非常] 快速的标准文本扫描中找不到任何证据。并不是说它会让我大吃一惊。
【解决方案2】:

如果你想给你的类的成员特定的值,总是使用构造函数。这就是构造函数的真正用途。唯一不需要编写设置所需值的构造函数的情况是,如果语言为您预先提供了一个,例如复制构造函数。 int 和其他此类类型的默认构造函数不在此列表中,因此您必须将构造函数添加到您自己的类型以适当地设置它们。

【讨论】:

  • 这似乎是没有限定词的严厉建议。添加用户定义的构造函数会以您在某些情况下可能不希望的方式更改类型的属性。
  • 抱歉,我删掉了“或者我应该只使用构造函数”,因为我真的很好奇为什么事情会这样发生。是的,我同意使用构造函数的代码会更好,但我想了解为什么它现在会这样工作
  • @Lightness:任何实际遇到其中一种情况并了解哪些属性已更改以及为什么不需要此答案的建议即可知道该怎么做的人。当你出于愚蠢的原因需要构造函数时不编写构造函数比不小心使你的类型成为非 POD 或其他东西更常见,也更糟糕。
  • 啊,我明白了:“这个答案故意省略了关键细节,因为任何阅读它的人都不会理解这些细节”。最好的学术势利!
【解决方案3】:

只要您没有任何构造函数,所有成员都是公共的,并且不涉及继承,您的结构/类很可能是一个聚合。

要获得可移植的零初始化,只需使用执行零初始化的X x = {};

这是关于聚合 (8.5.1) 的标准引用:

聚合是没有用户提供的数组或类(第 9 条) 构造函数(12.1),非静态没有大括号或等号初始化器 数据成员(9.2),没有私有或受保护的非静态数据成员 (第 11 条),没有基类(第 10 条),没有虚函数 (10.3)。

参考:Why can I not brace initialize a struct derived from another struct?

【讨论】:

    猜你喜欢
    • 2017-05-27
    • 2011-01-06
    • 2021-12-12
    • 2018-11-14
    • 2018-04-06
    • 2017-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多