Closed acgtyrant closed 9 years ago
当定义在类(class/struct)内部的数据成员为内置类型时,它也不被初始化吗?
我猜你想问的应该是 Non-static data members 且限制为内置类型的初始化规则,这个知识点会在 7.1.4 里准确介绍。
我举例简要说明:
如:
struct C { // or class
int n; // non-static data member
};
这里的 n 将会被初始化为 0,但它是通过 默认构造函数 进行初始化的。如果你自定义了构造函数(舍弃默认构造),则需要手动初始化,如:
struct C {
int n;
C() : n(0) {}
};
c++11 允许另一种方式对其进行初始化,即 brace-or-equal initializer 的方式,如:
struct C {
int n = 0;
C() {}
};
那么问题可能会来,如果两种初始化方式都用到了呢?如:
struct C {
int n = 0;
C() : n(1) {}
};
请问 n 初始化为多少?
答案是 1,两种方式并存之际,brace-or-equal initializer 会被忽略。
我们可以说这个内置类型在默认初始化中不被初始化,还是只能说它不被默认初始化呢?即前者相当于名词「阶段」,后者则如同动词「初始化」,我不知道 C++ 所谓的「默认初始化」到底该理解成哪一种语义。
抱歉,这一段没太看明白。看 @Mooophy 能否能理解。
「默认初始化」,即 default initialization,定义如下:
This is the initialization performed when a variable is constructed with no initializer.
这里面关键的两个名词的含义:initialization
和 initializer
,我还真的找不到应该用中文怎样描述清楚。。。
@acgtyrant 我手里只有电子版,电子版没有页码,贴下章节号。
@pezy
我知道构造函数机制,问题在于:
class A {
int a;
}
class A {
A() {};
int a;
}
这两个类当然一致,关键是它们对 a
没有提供类内初始值(in-class initializer),又没在构造函数里的初始值列表(member initializer list)初始化它。那么用 class A
声明了一个对象,后者的数据成员 a
将不被初始化(unitialized)吗,即其值未定义?
第二个问题我搞明白了。default initialization
就是一个名词,意思就是当对象未被显式赋予初始值时,就要对其执行的一种「行为」。但局部作用域的对象经过默认初始化后,其值是未定义的,也就是说这个「默认初始化(名词)」不保证「初始化(动词)其值」。
@Mooophy 「默认初始化」的相关章节是 2.2.1.
这两个类当然一致,关键是它们对 a 没有提供类内初始值(in-class initializer),又没在构造函数里的初始值列表(member initializer list)初始化它。那么用 class A 声明了一个对象,后者的数据成员 a 将不被初始化(unitialized)吗,即其值未定义?
这两个类当然不一致,第二个 A 的数据成员 a 未被初始化,其值是未定义;但第一个 A 的数据成员 a 是会被默认构造函数进行初始化的,其值是 0。
@pezy 但章节「7.1.4 构造函数」,中译版第 236 页,原版第 263 页指出:
编译器创建的构造函数又被称为合成的默认构造函数。对大多数类来说,这个合成的默认构造函数将按照如下规则初始化类的数据成员:
- 如果存在类内初始值,用它来初始化成员。
- 否则,默认初始化该成员。
我想了下,在我印象中,书上只提到过默认初始化在两种情况下不初始化内置类型变量,一是在函数体内,二是在局部作用域内。如果类的内部在语法上就是局部作用域,也没有对应的特殊规则,那么应该没有疑问了:第一个 A
的数据成员 a
其值未定义。
@acgtyrant @pezy 想讨论下第一个A的数据成员a其值是否是初始化为0的: 结论是,不确定,要根据A具化的对象的类型来判断:
#include <iostream>
using namespace std;
class A{
public:
int v;
A() {} // 1 -- 默认构造函数
};
A g_var;
int main(){
A l_var;
static A l_static;
cout<<g_var.v<<' '<<l_var.v<<' '<<l_static.v<<endl;
return 0;
}
执行结果:
sh-4.2$ g++ -std=c++11 -o main *.cpp
sh-4.2$ main
0 4197024 0
看到2.2.1 (p40)这部分的时候,我也存在跟题主相同的疑惑,也上网找了一些资料,这里推荐一篇自认为讲的比较清楚的博文,感觉讲的挺有道理的: http://harttle.com/2015/10/05/cpp-variable-init.html 在该文中,作者认为内置类型是否初始化为0,是和内置类型对应的变量类型或者说所在的存储空间类型决定的: 栈中的变量(函数体中的自动变量)和堆中的变量(动态内存)会保有不确定的值; 全局变量和静态变量(包括局部静态变量)会初始化为零 这里的观点和书中的观点是一致的。
class A{
public:
int v;
A() {} // 1 -- 默认构造函数
};
你这个写法, v 就是未定义。 跟 A 类在哪构造没关系。
@pezy 从上文g_var的执行结果看,v是初始化为0的,所以并非所有的v都是未定义的 且按照下面的写法,其执行结果和上述程序是一致的
#include<iostream>
using namespace std;
class A{
public:
int v;
};
A g_var;
int main(){
A l_var;
static A l_static;
cout<<g_var.v<<' '<<l_var.v<<' '<<l_static.v<<endl;
return 0;
}
执行结果:
sh-4.2$ g++ -std=c++11 -o main *.cpp
sh-4.2$ main
0 4196752 0
从上文g_var的执行结果看,v是初始化为0的,所以并非所有的v都是未定义的
什么叫做未定义(UB)?就是可以是任何值,C++ 语法不对其进行一致性保证。所以 g_var.v 的值是 0 不能说明其值就不是 UB.
class A{
public:
int v;
};
针对这种情况, 请注意我的之前的回复: 但第一个 A 的数据成员 a 是会被默认构造函数进行初始化的,其值是 0。
也就是说, 你得这么写, 才可以确保值为0:A l_var{};
~你代码中其他的写法, 其值皆为 UB。~
也就是说, 你得这么写, 才可以确保值为0:A l_var{};
你代码中其他的写法,其值皆为 UB。
@pezy 谢谢回复,按照上面的讨论我做了如下验证
#include <iostream>
using namespace std;
class A{
public:
int *v;
};
A g_var;
int main() {
A l_var{};
static A l_static;
cout<<g_var.v<<' '<<l_var.v<<' '<<l_static.v<<endl;
return 0;
}
执行结果:
sh-4.2$ g++ -std=c++11 -o main *.cpp
sh-4.2$ main
0 0 0
关于结果有如下两个疑问,希望帮忙解答下
关于A l_var{};的理解,的确如pezy所说,这里可以正确初始化为0: cp中指出:定义在函数内部的内置类型变量将不被初始化(Uninitialized). 所以我对这里v初始化为0存在一定的疑惑: A l_var{}; 和 A l_var; 这两种定义方式的本质区别在哪里,在我的理解中一直认为二者是一样的(都会调用默认构造函数,都不会初始化v,难道说v不算是局部变量?), 这里希望大神能够给出详细的解答,先谢下
关于 "你代码中其他的写法,其值皆为 UB" 这个说法,我故意在代码中使用int v的方式来验证: 结果显示,全局变量和静态变量中的v都正确初始化为0了(我认为这里使用int 是能够区分UB和初始化为0(NULL)的), 所以我理解:对于全局变量和静态变量,c++是能够保证其内部内置类型成员被正确初始化为0的,并非UB, 这点也请pezy确认下
这两种定义方式的本质区别在哪里
不一样, 前者明确是 zero-initialized, 后者仅仅是调用缺省构造函数(缺省构造函数的行为可参考这里)。
难道说v不算是局部变量?
v 是一个成员变量。
所以我理解:对于全局变量和静态变量,c++是能够保证其内部内置类型成员被正确初始化为0的,
是的, 这一点我理解有偏差, 对于静态和全局变量而言, 始终会发生 zero initialization (包括其全部成员。)
For all other non-local static and thread-local variables, Zero initialization takes place. --- http://en.cppreference.com/w/cpp/language/initialization
@pezy thx
中译版第 40 页则指出:
问题有两个:
原版第 44 页开头即是相关文。