C++ 未初始化的数据行为是否已正确指定?

C++ 未初始化的数据行为是否已正确指定?,c++,initialization,C++,Initialization,注意:我正在使用g++编译器(我听说它非常好,应该非常接近标准) 我有我能想到的最简单的课程: class BaseClass { public: int pub; }; 然后我有三个同样简单的程序来创建BaseClass对象,并打印出它们的数据的[uninitialized]值 案例1 这很好。我实际上认为它会被初始化为零,因为它是一个POD或普通的旧数据类型或类似的东西,但我想不是吗?到目前为止还不错 案例2 这确实很奇怪。我用同样的方法创建了两个相同的对象。一个被初始

注意:我正在使用g++编译器(我听说它非常好,应该非常接近标准)


我有我能想到的最简单的课程:

class BaseClass  {
  public:
    int pub;
};
然后我有三个同样简单的程序来创建
BaseClass
对象,并打印出它们的数据的[uninitialized]值


案例1 这很好。我实际上认为它会被初始化为零,因为它是一个POD或普通的旧数据类型或类似的东西,但我想不是吗?到目前为止还不错


案例2 这确实很奇怪。我用同样的方法创建了两个相同的对象。一个被初始化,另一个没有


案例3 这是迄今为止最奇怪的。它们都被初始化为零。我所做的只是添加了两行代码,它改变了以前的行为


那么,这只是“未初始化的数据导致未指定的行为”的一种情况,还是“在幕后”发生了更符合逻辑的事情

我真的很想了解默认构造函数/析构函数的行为,因为我觉得它对于完全理解继承内容非常重要

那么,这只是“未初始化的数据导致未指定的行为”的情况吗

是的

有时,如果您调用
malloc
(或
new
,调用
malloc
),您将获得填充有零的数据,因为它位于内核的新页面中。其他时候,它将充满垃圾。如果你在堆栈上放了一些东西(例如,自动存储),你几乎肯定会得到垃圾——但这可能很难调试,因为在你的系统上,垃圾可能恰好是可以预测的。对于堆栈上的对象,您会发现在完全不同的源文件中更改代码可以更改在未初始化的数据结构中看到的值

关于豆荚:关于豆荚是否是豆荚的问题,在这里真的是一条红鲱鱼。我之所以解释这个问题,是因为这个问题提到了POD,而谈话就此中断了。这两个相关概念是存储持续时间和构造函数。POD对象没有构造函数,但没有构造函数的东西不是POD。(从技术上讲,POD对象既没有非平凡构造函数,也没有具有非平凡构造函数的成员。)

存储时间:有三种。静态持续时间用于全局变量,自动持续时间用于局部变量,动态持续时间用于堆上的对象。(这是一个简化,并不完全正确,但是如果你需要正确的东西,你可以自己读C++标准。) 任何具有静态存储持续时间的内容都将初始化为零。因此,如果您创建一个
BaseClass
的全局实例,那么它的
pub
成员将是零(首先)。因为您将它放在堆栈和堆上,所以这个规则不适用——并且您不做任何其他事情来初始化它,所以它是未初始化的。它碰巧包含了最后一段代码使用它时在内存中留下的任何垃圾

通常,除非您自己初始化,否则堆或堆栈上的任何POD都将被取消初始化,并且该值将是未定义的,在重新编译或再次运行程序时可能会发生更改。通常,任何全局POD都将被初始化为零,除非您将其初始化为其他内容


检测未初始化值:尝试使用Valgrind的memcheck工具,它将帮助您找到使用未初始化值的位置-这些通常是错误。

按照您编写类的方式,
pub
的值未定义,可以是任何值。如果您创建一个将调用
pub
的默认构造函数的默认构造函数,它将保证为零:

class BaseClass  {
  public:
    BaseClass() : pub() {}; // calling pub() guarantees it to be zero.
    int pub;
};

这将是一个更好的实践。

一般来说,是的,未初始化的数据会导致未指定的行为。这就是为什么像C#这样的其他语言会采取措施确保您不会使用未初始化的数据。

这就是为什么您总是、总是、总是将类(或任何变量)初始化为稳定状态,而不是依赖编译器来为您执行;特别是一些编译器故意用垃圾填充它们。事实上,MSVC++唯一不填充垃圾的POD是
bool
,它们被初始化为
true
。有人可能会认为将其初始化为false会更安全,但这是Microsoft为您所做的。

案例1 无论封装类型是否为POD,内置类型的数据成员都不会由封装默认构造函数进行默认初始化

案例2 没有,两个都没有草签。其中一个的内存位置的底层字节恰好是
0

案例3 一样


您似乎期望对未初始化对象的“值”有一些保证,同时表示理解不存在此类值。

在所有三种情况下,这些POD对象可能具有不确定的值

默认值下,没有任何初始值设定项的POD对象将不会初始化。它们只包含垃圾。

来自标准8.5初始值设定项

如果没有为对象指定初始值设定项,并且该对象为 (可能是cv限定的)非POD类类型(或其数组) 对象应默认初始化;如果对象为 const限定类型,基础类类型应具有 用户声明了默认构造函数。否则,如果未使用初始值设定项 为非静态对象、对象及其子对象指定,如果 任何,具有不确定的初始值;如果对象或 其子对象为常量限定类型,程序为 格式不正确。”

可以像这样零初始化POD结构的所有成员

BaseClass object={0};

这取决于您如何申报:

// Assuming the POD type's
class BaseClass
{
  public:
    int pub;
};
静态存储持续时间对象 这些对象始终为零初始化

// Static storage duration objects:
// PODS are zero initialized.

BaseClass    global; // Zero initialized pub = 0

void plop()
{
    static BaseClass functionStatic;   // Zero initialized.
}
自动/动态Sto
BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;
BaseClass* pB3 = new BaseClass;
cout<<"B3.pub = "<<pB3->pub<<endl;
B1.pub = 0
B2.pub = 0
B3.pub = 0
class BaseClass  {
  public:
    BaseClass() : pub() {}; // calling pub() guarantees it to be zero.
    int pub;
};
BaseClass object={0};
// Assuming the POD type's
class BaseClass
{
  public:
    int pub;
};
// Static storage duration objects:
// PODS are zero initialized.

BaseClass    global; // Zero initialized pub = 0

void plop()
{
    static BaseClass functionStatic;   // Zero initialized.
}
void plop1()
{
    // Dynamic
    BaseClass*  dynaObj1   = new BaseClass;   // Default initialized (does nothing)
    BaseClass*  dynaObj2   = new BaseClass(); // Zero Initialized

    // Automatic
    BaseClass   autoObj1;                     // Default initialized (does nothing)
    BaseClass   autoObj2   =     BaseClass(); // Zero Initialized

    // Notice that zero initialization of an automatic object is not the same
    // as the zero initialization of a dynamic object this is because of the most
    // vexing parse problem

    BaseClass    autoObj3(); // Unfortunately not a zero initialized object.
                             // Its a forward declaration of a function.
}