为什么支撑初始化分配分配填充垃圾变量



我已经陷入了这样的信念,即使用Brace初始化时会为变量分配其默认值。但是我错了。

在以下示例中:

#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. 如果一个类的成员变量是支撑限制的,则将分配0或默认值。例如:

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

有人可以解释为什么会发生这种情况吗?我在这里缺少什么?

a = {};是一个分配,a是从{}构造的临时对象分配的。隐式生成的分配将对所有数据成员执行成员的分配,那么关键是如何从{}初始化临时对象。

这是副本列表限制,因为效果,进行了价值定位。

否则,如果支撑式列表为空,而T是具有默认构造函数的类类型,则进行Value-Initialization。

作为价值定性的影响,

1(如果t是没有默认构造函数的类类型或用户提供或删除的默认构造函数,则该对象是默认的initialized;

A具有用户提供的默认构造函数,并且作为默认初始化的效果,该默认构造函数用于初始化临时对象。用户提供的默认构造器的主体为空的,对于临时对象,var4将通过std::string的默认构造函数默认initialial,所有其他具有Build-In类型类型的数据成员都将具有不确定的值。

  1. 如果我摆脱了默认构造函数 - 问题消失了。

然后,价值定位的行为将变为

(强调我的(

2(如果t是具有默认构造函数的类类型,该类型既不提供用户提供也不删除(也就是说,它可能是具有隐式定义或默认默认型构建器的类对象为零initialized ,然后如果它具有非平凡的默认构造函数,则将其默认限制化;

请注意,此处的差异,临时对象首先将被零定位。然后,所有具有建筑类型的数据成员都将初始化为0(var4仍被默认initialized(。

  1. 如果类的成员变量是支撑式化的,则将其分配给0或默认值。

这是默认初始化器列表的工作方式。

通过默认成员初始化器,这仅是成员声明中包含的支撑或等于初始化器,如果在成员初始化器列表中省略了会员

中使用该成员。

然后所有数据成员都是由指定的初始化器初始化的;在您的示例中,它们都是价值限制的,因为效果var4被默认为initialized,其他成员对0进行了零命名。

a = {};

此行并不意味着类内的所有变量都获得{} initializer。相反,它调用(未定义,因此自动生成(副本(或移动(分配操作员,该操作员从用{}(即具有非注重变量(创建的对象进行浅复制/移动到您拥有的对象。

var4 似乎要清除,但实际上是从新对象var4复制/移动的,并且由于std :: string具有默认的构造函数,因此是空的。<<<<<<<<<<<<<<<<

避免类似内容的简单解决方案是初始化班级中的非类变量,例如

class A
{
    int var = 0;
    ...
};

最新更新