在C++中,以下短语的含义是什么:零初始化、默认初始化和值初始化?

210
以下短语在C++中的含义是什么:
  • 零初始化(zero-initialization)

  • 默认初始化(default-initialization)

  • 值初始化(value-initialization)

C++开发人员需要了解它们的哪些方面?


1
这与https://dev59.com/_3RB5IYBdhLWcg3wcm6d有关(但并非完全相同)。 - Steve Jessop
26
还有更多!完整的初始化列表包括:Value(值初始化)、direct(直接初始化)、copy(拷贝初始化)、list(C++11 中新增的列表初始化)、aggregate(聚合类初始化)、reference(引用初始化)、zero(零值初始化)、constant(常量初始化)和default(默认初始化);http://en.cppreference.com/w/cpp/language/initialization 列出了它们的所有示例 :) - legends2k
@legends2k 这很好,谢谢!我希望有人能谈论一下更新的标准! - ZoomIn
2个回答

91
C++03标准8.5/5:
对于类型为T的对象进行零初始化意味着: - 如果T是标量类型(3.9),则将对象设置为值为0(零)转换为T的值; - 如果T是非联合类类型,则每个非静态数据成员和每个基类子对象都将被零初始化; - 如果T是联合类型,则该对象的第一个命名数据成员将被零初始化; - 如果T是数组类型,则每个元素都将被零初始化; - 如果T是引用类型,则不执行初始化。
对于类型为T的对象进行默认初始化意味着: - 如果T是非POD类类型(第9条款),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化无效); - 如果T是数组类型,则每个元素都将被默认初始化; - 否则,对象将被零初始化。
对于类型为T的对象进行值初始化意味着: - 如果T是具有用户声明构造函数(12.1)的类类型(第9条款),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化无效); - 如果T是没有用户声明构造函数的非联合类类型,则T的每个非静态数据成员和基类组件都将进行值初始化; - 如果T是数组类型,则每个元素都将进行值初始化; - 否则,对象将被零初始化。
调用引用类型实体的默认初始化或值初始化的程序是无效的。如果T是带有cv限定符的类型,则这些零初始化、默认初始化和值初始化的定义使用T的cv非限定版本。

24
这可能已经过时了对于C++11。cppreference.com指出,默认初始化不会将成员变量清零(只有值初始化才会这样做)。 - Alexei Sholik
5
@android提出了一个重要的问题,在其他地方我没有看到对其进行回答,因此我创建了一个新的问题。https://dev59.com/_2Eh5IYBdhLWcg3wnEYc - Adrian McCarthy

67

需要注意的一点是,“value-initialization”是C++ 2003标准中新增的——在原始的1998标准中不存在(我认为这可能是唯一一个不仅仅是澄清的差异)。请参见Kirill V. Lyadvinsky的答案,其中包含了标准定义。

关于这些初始化类型的不同行为以及它们何时启用(以及它们从C++98到C++03的不同之处),请参见此前的回答:operator new 的行为细节:

该回答的主要观点是:

有时,由new运算符返回的内存将被初始化,有时则不会,这取决于您正在创建的类型是否为POD,或者它是一个包含POD成员并正在使用编译器生成的默认构造函数的类。

  • C++1998有两种初始化类型:零初始化和默认初始化
  • C++2003新增了第三种初始化类型:值初始化。

不言而喻,这相当复杂,不同方法启动的时间也很微妙。

需要特别注意的一点是,MSVC即使在VS 2008(VC 9或cl.exe版本15.x)中仍遵循C++98规则。

以下代码段显示了MSVC和Digital Mars遵循C++98规则,而GCC 3.4.5和Comeau遵循C++03规则:

#include <cstdio>
#include <cstring>
#include <new>

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

int main()
{
    char buf[sizeof(B)];
    std::memset( buf, 0x5a, sizeof( buf));

    // use placement new on the memset'ed buffer to make sure 
    //  if we see a zero result it's due to an explicit 
    //  value initialization
    B* pB = new(buf) B();   //C++98 rules - pB->m is uninitialized
                            //C++03 rules - pB->m is set to 0
    std::printf( "m  is %d\n", pB->m);
    return 0;
}

2
虽然对于 int 来说并不重要,但是在第三行中 m() 的值初始化了 m。如果你将 int m; 改为 B m;,这一点就很重要了。 :) - Johannes Schaub - litb
1
我的意思是,如果你将C更改为struct C { C() : m() {}; ~C(); B m; };,那么你将会得到m.m为0。但是,如果它像你所说的C++03一样默认初始化m,那么m.m将不会像在C++98中一样被初始化。 - Johannes Schaub - litb
1
以下是有关MSVC处理此功能的其他有趣评论:https://dev59.com/Xm865IYBdhLWcg3wNLw7#3931589 - Brent Bradburn
1
g++ 4.4.7 20120313适用于Red Hat 4.4.7-18,在使用示例(编译时使用-std=c++98)初始化m为0。 - Jean
g++ 10.2.0 for Ubuntu使用您的示例(使用-std=c++98编译)将m初始化为0。 - HEKTO
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接