【问题标题】:C++ Zero-InitializationC++ 零初始化
【发布时间】:2018-07-26 08:47:47
【问题描述】:

根据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;
}

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

但是,在我的带有g++ -m32 -o test -std=c++14 test.cpp &amp;&amp; test 机器的 Windows 10 机器上使用 g++ 5.3.0 编译会得到输出:

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 = {}; 声明我的对象堆栈时,它没有对堆栈进行零初始化?

【问题讨论】:

  • 我不符合你的期望。显然,您链接的文档页面中的三点都不适用。
  • MyTest testObj = {} 不是零初始化。它是值初始化,它只是调用默认构造函数,在您的情况下它什么都不初始化。
  • MyTest() {} 构造函数没有初始化任何东西。
  • 这种行为是否特定于函数指针?
  • 注意:6.6.2 Static initialization 静态存储持续时间的对象初始化为零。这就是为什么当你把它设为全局变量时,一切都被初始化为零。自动对象遵循稍微复杂的规则。

标签: c++ c++14 zero-initialization


【解决方案1】:

以下

MyTest testObj = {};

不是MyTest 进行零初始化,而只是调用其默认构造函数。 cppreference 页面解释了原因(强调我的):

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

MyTest是类类型,a有构造函数。


定义构造函数

MyTest() = default;

将改为零初始化对象。

下面的相关标准引用(强调我的)。

来自[dcl.init#8]

对 T 类型的对象进行值初始化意味着:

  • 如果 T 是没有默认构造函数 ([class.ctor]) 或用户提供或删除的默认构造函数的(可能是 cv 限定的)类类型,则该对象是默认初始化的;

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

  • ...

来自[dcl.init.list]

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

  • ...

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

【讨论】:

  • 啊,谢谢。连同值初始化部分,我现在也明白了为什么,当我注释掉类中的构造函数时,当我声明为 MyTest testObj ; 时它会打印相同的未初始化值,而当我做 MyTest testObj = {}; 时它会打印全零
  • 等一下。如果没有显式提供构造函数,所有指针都应该为零吗?还是我错了?
  • @bartop 弄错了,抱歉,已更正。
  • @bartop 你错了int main(){int* p;} - 这里的p 不能保证是nullptr。它是一个值不确定的未初始化指针。
  • @Vittorio:如果你用= default声明默认构造函数,会导致它被初始化值吗?
猜你喜欢
  • 1970-01-01
  • 2014-12-29
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 2016-02-01
  • 2012-12-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多