C++零初始化

31

我不太明白,按照http://en.cppreference.com/w/cpp/language/zero_initialization的规定,什么情况下我的类中的成员被初始化为零。

请看下面这个测试程序:

#include <iostream>
#include <stdio.h>

class MyTest {
private:
    const static unsigned int dimension = 8;
    void (* myFunctions [dimension])();

public: 
    MyTest() {}

    void print() { 
        for(unsigned int i=0; i < MyTest::dimension; i++) {
            printf("myFunctions[%d] = %p\n", i, this->myFunctions[i]);
        }   
    }
};


int main() {
    //We declare and initialize an object on the stack 
    MyTest testObj = {};
    testObj.print();

    return 0;
}

我声明一个类具有一个由8个函数指针组成的数组,其签名为“void functionname()”。当我在main中声明并初始化一个类对象,如MyTest testObj = {};MyTest testObj;时,我期望它被零初始化,即所有指针都是空指针。

然而,在我的Windows 10机器上使用g++ 5.3.0编译,命令为g++ -m32 -o test -std=c++14 test.cpp && test, 输出结果为:

myFunctions[0] = 76dd6b7d
myFunctions[1] = 00401950
myFunctions[2] = 0061ff94
myFunctions[3] = 004019ab
myFunctions[4] = 00401950
myFunctions[5] = 00000000
myFunctions[6] = 003cf000
myFunctions[7] = 00400080

看起来像是从堆栈中未初始化的值...

如果我将对象的声明移到main函数外面(作为全局变量),它会再次打印所有的零。 如果我正确理解了cppreference,这是因为我有一个具有静态存储期限的变量,因此它被零初始化。 它通过零初始化我的类类型来初始化所有非静态数据成员(即myFunctions数组)。 数组通过零初始化它的每个元素,对于我的函数指针情况,它是一个空指针。

为什么当我用MyTest testObj = {};声明时它不会在堆栈上将我的对象初始化为零?


5
我不理解你的期望。 显然,你提供的文档页面中的三个要点都不适用。 - Baum mit Augen
8
"MyTest testObj = {}" 不是零初始化,它是值初始化,并且它只是调用默认构造函数,在你的情况下不会进行任何初始化。 - Igor Tandetnik
5
MyTest() {} 构造函数不会初始化任何东西。 - Eljay
这种行为是特定于函数指针的吗? - Ulrich Eckhardt
注意:6.6.2 静态初始化 具有静态存储期的对象将被零初始化。这就是为什么当你将它作为全局变量时,所有内容都会被初始化为零。自动对象遵循稍微复杂一些的规则。 - Martin York
1个回答

43

The following

MyTest testObj = {};

对于MyTest,并非零初始化,而只是调用其默认构造函数。 cppreference页面解释了原因(重点在于):

作为值初始化序列的一部分适用于非类类型和没有构造函数的值初始化类类型的成员,包括聚合体元素的值初始化,其中未提供初始化程序。

MyTest是一个类类型,并且具有构造函数。


使用以下方式定义构造函数

MyTest() = default;

将会进行零值初始化

下面是相关标准的引用(我添加了强调):

来自[dcl.init#8]

对于类型T,对其进行值初始化的意思是:

  • 如果T是一个(可能带有cv限定符的)类类型,没有默认构造函数([class.ctor])或者该类的默认构造函数为用户自定义或已删除,则对象将被默认初始化;

  • 如果T是一个(可能带有cv限定符的)类类型,并且没有用户自定义或已删除的默认构造函数,则对象将被零值初始化,检查默认初始化的语义约束,如果T具有非平凡的默认构造函数,则对象将被默认初始化;

  • ...

来自[dcl.init.list]

类型T的对象或引用的初始化列表如下所示:

  • ...

  • 否则,如果初始化列表中没有元素并且T是具有默认构造函数的类类型,则对象将被值初始化。


啊,谢谢。现在我明白了为什么当我在类中注释掉构造函数时,声明为 MyTest testObj ; 时打印相同的未初始化值,而当我使用 MyTest testObj = {}; 时打印全零值。同时,我也理解了值初始化部分。 - Maximilian Gerhardt
等一下。如果没有显式提供构造函数,所有指针应该都为零?或者我错了? - bartop
@bartop,我搞错了,抱歉,已经更正。 - Maximilian Gerhardt
6
@bartop,你错了。在这里int main(){int* p;}中,p并不保证是nullptr。它是一个未初始化的指针,具有不确定的值。 - Jesper Juhl
@Vittorio:如果你使用= default声明默认构造函数,会导致它被值初始化吗? - Nicol Bolas
显示剩余10条评论

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