为什么大括号初始化赋值会用垃圾填充变量?

Why does brace initialization assignment fill variables with garbage?

我相信在使用大括号初始化时变量被赋予了默认值。但是我错了。

在下面的例子中:

#include <string>
#include <iostream>

#include <stdint.h>


class A
{
public:
    A() {}
    ~A(){}

    int var1;
    int32_t var2;
    int64_t var3;
    std::string var4;
    double var5;
    float var6;

    std::string info() const {
        return "var1=" + std::to_string(var1) + " " +
               "var2=" + std::to_string(var2) + " " +
               "var3=" + std::to_string(var3) + " " +
               "var4=" + var4 + " " +
               "var5=" + std::to_string(var5) + " " +
               "var6=" + std::to_string(var6) + " " +
               "\n"
               ;
    }
};

int main()
{
    A a;
    std::cout << "Before assigning variables: " << a.info();

    a.var1 = 1;
    a.var2 = 2;
    a.var3 = 3;
    a.var4 = "4";
    a.var5 = 5;
    a.var6 = 6;
    std::cout << "After assigning variables: " << a.info();

    a = {};
    std::cout << "After brace init assignment: " << a.info();
}

结果如下:

Before assigning variables: var1=0 var2=0 var3=4198240 var4= var5=0.000000 var6=0.000000 
After assigning variables: var1=1 var2=2 var3=3 var4=4 var5=5.000000 var6=6.000000 
After brace init assignment: var1=2114725200 var2=32766 var3=4199416 var4= var5=0.000000 var6=0.000000

修复:

  1. 如果我去掉默认构造函数 - 问题就会消失。
  2. 如果class的成员变量是大括号初始化的,那么它会被赋值为0或默认值。 例如:

    class A
    {
    public:
        A() {}
        ~A(){}
    
        int var1{};
        int32_t var2{};
        int64_t var3{};
        std::string var4{};
        double var5{};
        float var6{};
    };
    

谁能解释一下为什么会这样?我在这里错过了什么?

a = {};

这一行并不意味着 class 中的所有变量都获得一个 {} 初始值设定项。 它改为调用(未定义,因此自动生成)复制(或移动)赋值运算符,该运算符从使用 {} 创建的对象(即未初始化的变量)到您拥有的对象执行浅层 copy/move。

var4 好像被清除了,其实是copied/moved来自新对象var4,而且由于std::string有一个默认的构造函数,它是空的。

避免这种情况的简单解决方案是在 class 中初始化 non-class 变量,比如

class A
{
    int var = 0;
    ...

};

a = {};是赋值,a是从{}构造的临时对象赋值。 implicitly-generated 赋值将对所有数据成员执行 member-wise 赋值,然后重点是如何从 {}.

初始化临时对象

这是copy-list-initialization, as the effect, value-initialization执行的

Otherwise, If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.

作为value-initialization,

的效果

1) if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;

A 有一个 user-provided 默认构造函数,并且由于 default initialization, that default constructor is used to initialize the temporary object. The user-provided default constructor's body is empty, then for the temporary object, var4 will default-initialized 受到 std::string 默认构造函数的影响,所有其他数据成员都具有 build-in 类型将具有不确定的值。

  1. If I get rid of default constructor - the problem goes away.

那么 value-initialization 的行为将变为

(强调我的)

2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;

注意这里的区别,临时对象将是 zero-initialized at first. Then all the data members with build-in type are initialized to 0 (var4 is still default-initialized).

  1. If class's member variable is brace-initialized, then it will be assigned to 0 or default value.

这就是 default initializer list 的工作原理。

Through a default member initializer, which is simply a brace or equals initializer included in the member declaration, which is used if the member is omitted in the member initializer list

然后所有的数据成员都被指定的初始化器初始化;在您的示例中,它们都是 value-initialized, as the effect, var4 is default-initialized, other members are zero-initialized0