为什么用户定义 class 类型的静态和局部变量的合成默认构造函数有不同的行为?

Why different behaviour of synthesized default constructor for static and local variable of user defined class type?

在下面的示例程序中,

为什么用户定义的 class 类型的静态变量和自动变量的输出不同?

/* test.cpp */

/* SalesData Class */
class SalesData {

    public:
        SalesData() = default;
        // other member funcations
    private:
        std::string bookNo;
        unsigned int unitsSold;
        double revenue;

};

/*
 * Prints the SalesData object
 */
std::ostream& print(std::ostream &os, const SalesData &item) {
    os << "ISBN :\'" << item.isbn() << "\', Units Sold :" << item.unitsSold
        << ", Revenue :" << item.revenue << ", Avg. Price :"
        << item.avgPrice() << std::endl;

    return os;
}

int main(int argc, char *argv[]) {
    SalesData s;
    static SalesData s2;


    print(cout, s);
    print(cout, s2);

    return 0;
}

程序的输出是这样的--

$ ./test
ISBN :'', Units Sold :3417894856, Revenue :4.66042e-310, Avg. Price :1.36352e-319
ISBN :'', Units Sold :0, Revenue :0, Avg. Price :0

静态如何改变合成默认构造函数中的场景?

默认构造函数在 ss2 上具有相同的行为。不同的是,对于static local variables

Variables declared at block scope with the specifier static or thread_local (since C++11) have static or thread (since C++11) storage duration but are initialized the first time control passes through their declaration (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered).

大约 zero-initialization:

For every named variable with static or thread-local (since C++11) storage duration that is not subject to constant initialization, before any other initialization.

这意味着s2将首先被零初始化,因为它的数据成员也被零初始化;然后输入 main() 它是通过默认构造函数默认初始化的。

具有静态存储持续时间的变量在其动态初始化阶段之前被零初始化。

unitsSoldrevenue 是默认初始化的。这使它们未初始化。在静态存储的情况下,先前的零初始化保持不变。在自动存储的情况下,它们具有不确定的值。读取不确定的值会导致程序出现未定义的行为。

案例一

让我们考虑 SalesData s; .

在这种情况下,使用 SalesData 默认构造函数 构造名称为 s 的类型 SalesData 的对象。另外,请注意,在这种情况下,变量 slocal nonstatic.

下一个是你:

  1. 没有使用in-class初始化器来初始化unitsSoldrevenue
  2. 没有使用构造函数初始化列表来初始化unitsSoldrevenue

这 2 个变量持有 不确定值。使用这些未初始化的变量会导致未定义的行为

这意味着当你写道:

print(cout, s);//this calls print function

print 函数体内你有:

os << "ISBN :\'" << item.isbn() << "\', Units Sold :" << item.unitsSold
        << ", Revenue :" << item.revenue << ", Avg. Price :"
        << item.avgPrice() << std::endl; //this is undefined behavior

所以在上面的语句中你使用了未初始化变量unitsSoldrevenue,这导致未定义的行为.

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior.

这就是您在本例中看到像 34178948564.66042e-310 这样的数字(垃圾值)的原因。所以不要依赖你的程序的输出,因为它有未定义的行为。

案例二

现在让我们考虑static SalesData s2;

来自 Static local variables' initialization documentation:

Variables declared at block scope with the specifier static or thread_local (since C++11) have static or thread (since C++11) storage duration but are initialized the first time control passes through their declaration (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered).

现在 zero initialization documentation:

Zero initialization is performed in the following situations:

  1. For every named variable with static or thread-local (since C++11) storage duration that is not subject to constant initialization, before any other initialization.

这里是最相关的部分:

零初始化的效果是:

If T is an non-union class type, all base classes and non-static data members are zero-initialized, and all padding is initialized to zero bits. The constructors, if any, are ignored.

让我们将上面引用的这些语句应用到您的示例中 static SalesData s2;

在这种情况下,变量 s2 是一个 本地静态变量 。这里发生了两件事:

  1. 变量s2初始化为零。这意味着它的所有非静态数据成员如 unitsSoldrevenue 都根据上面引用的语句设置为 0。这就是为什么在这种情况下输出数字都是 0 而不是 一些垃圾值的原因。
  2. 接下来使用默认构造函数.
  3. 默认初始化变量s2

1有关未定义行为的更技术准确的定义,请参阅 this 其中提到:没有对程序行为的限制.