在下面的示例程序中,
为什么静态&用户定义类类型的自动变量?
/* 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
静态如何在合成默认构造函数中更改场景?
默认构造函数在s
和s2
上具有相同的行为。区别在于,对于静态局部变量,
在块作用域中用说明符static
or thread_local (since C++11)
声明的变量具有静态or thread (since C++11)
存储持续时间,但在控件第一次通过其声明时进行初始化(除非其初始化为零或常量初始化,这可以在首次输入块之前执行)。
和关于零初始化:
对于每个具有静态
or thread-local (since C++11)
存储持续时间的命名变量,在任何其他初始化之前,该变量不受常量初始化的约束。
这意味着s2
将首先被零初始化,因为它的数据成员也被零初始化;然后进入CCD_ 7,它通过默认构造函数进行默认初始化。
案例I
让我们考虑SalesData s;
。
在这种情况下,使用SalesData
的默认构造函数构造名为s
的类型为SalesData
的对象。此外,请注意,在这种情况下,变量s
是局部非静态。
自你以来的下一个:
-
没有使用类内初始化程序初始化
unitsSold
和revenue
-
没有使用构造函数初始值设定项列表初始化
unitsSold
和revenue
这两个变量具有不确定值。使用这些未初始化的变量会导致未定义的行为。
这意味着当你写:
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
因此,在上面的语句中,您使用了未初始化的变量unitsSold
和revenue
,这会导致出现未定义的行为。
未定义的行为意味着任何1都可能发生,包括但不限于提供预期输出的程序。但是永远不要依赖(或根据未定义行为的程序的输出得出结论)。
这就是您在本例中看到像3417894856
和4.66042e-310
这样的数字(垃圾值)的原因。因此,不要依赖程序的输出,因为它有未定义的行为。
情况II
现在让我们考虑static SalesData s2;
来自静态局部变量的初始化文档:
在块作用域中使用说明符static或thread_local(自C++11以来)声明的变量具有静态或线程(自C++11起(除非它们的初始化是零或常量初始化,这可以在首次输入块之前执行)。
现在从零开始初始化文档:
在以下情况下执行零初始化:
- 对于每个具有静态或线程本地(自C++11以来)存储持续时间的命名变量,在任何其他初始化之前,该变量不受常量初始化的影响
下面是最相关的部分:
零初始化的影响为:
如果T是非并集类类型,则所有基类和非静态数据成员都被零初始化并且所有填充都被初始化为零位。构造函数(如果有)将被忽略。
让我们将以上引用的语句应用于您的示例static SalesData s2;
在这种情况下,变量s2
是局部静态。这里发生了两件事:
- 变量
s2
被初始化为零。这意味着它的所有非静态数据成员,如unitsSold
和revenue
,都根据上面引用的语句设置为0
。这就是为什么这种情况下的输出数字都是0
,而不是一些垃圾值 - 接下来,变量
s2
被默认初始化,使用缺省构造函数
1对于未定义行为的更准确的技术定义,请参阅此处,其中提到:对程序的行为没有限制
具有静态存储持续时间的变量在其动态初始化阶段之前进行零初始化。
CCD_ 31和CCD_。这让他们变得不熟悉。在静态存储的情况下,先前的零初始化保持不变。在自动存储的情况下,它们的值不确定。读取不确定的值会导致程序的未定义行为。