为什么大括号初始化赋值会用垃圾填充变量?
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
修复:
- 如果我去掉默认构造函数 - 问题就会消失。
如果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.
的效果
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 类型将具有不确定的值。
- 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).
- 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-initialized 到 0
。
我相信在使用大括号初始化时变量被赋予了默认值。但是我错了。
在下面的例子中:
#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
修复:
- 如果我去掉默认构造函数 - 问题就会消失。
如果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.
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 类型将具有不确定的值。
- 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).
- 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-initialized 到 0
。