std::cout是否保证被初始化?
std::cout是否保证被初始化?
在C++中,不能假设全局实例的构造和析构顺序。\n当我在编写一个使用std::cout
在构造函数和析构函数中的全局实例的代码时,我有一个问题。\nstd::cout
也是iostream的一个全局实例。是否保证在其他全局实例之前初始化std::cout
?\n我写了一个简单的测试代码,它完美地工作了,但是我仍然不知道为什么。\n
#includestruct test { test() { std::cout << "test::ctor" << std::endl; } ~test() { std::cout << "test::dtor" << std::endl; } }; test t; int main() { std::cout << "Hello world" << std::endl; return 0; }
\n它会打印出:\n
test::ctor Hello world test::dtor
\n是否有可能代码运行不如预期?
《C++标准库》中的§27.3/2规定了关于std::cin、std::cout等对象的构造和关联建立的时间,即在类ios_base::Init的对象被构造之前或者在其构造期间,并且在main函数体开始执行之前。然而,这并没有说明std::cout相对于其他静态对象的构造顺序。
虽然如此,脚注265(从§27.3/2引用)表示应该能够正常工作:“静态对象的构造函数和析构函数可以访问这些对象,从stdin读取输入或者向stdout或stderr输出。”虽然这可能不是规范性的说明,但至少明确了代码应该可以正常工作的意图。
实际上确实如此。段落继续说明:“在一个翻译单元中包含
那么,如果在#include
如果它们使用了std::cout,那么它们必须自己包含#include
你忘记了独立编译模型,仅仅因为一个头文件创建了一个静态/全局变量,并不意味着该变量的构造函数被内联在头文件中!它可以被放在一个cpp文件中(该cpp文件包含了#include
我同意,但这也不是我的重点。标准确实没有指定std::cout何时被初始化。它只是规定了对于特定的翻译单元,结果应该是“好像”在该头文件中定义了一个实例。这只是一种用于保证即使在静态对象中也可以安全使用std::cout的标准方式。
在C++03和C++11中,关于std::cout是否保证初始化的答案是不同的。在C++11中,你的代码被保证可以工作,但在C++03中是未指定的;你唯一的保证是在进入main()函数之前,标准流已经被初始化。(也就是说,所有主流的实现在运行任何动态初始化之前都会初始化它们,使它们可以正常使用。)
你可以通过构造一个std::ios_base::Init对象来强制初始化,代码如下:
#includestruct test { test() { std::cout << "test::ctor" << std::endl; } ~test() { std::cout << "test::dtor" << std::endl; } private: std::ios_base::Init mInitializer; }; test t; int main() { std::cout << "Hello world" << std::endl; return 0; }
现在当test对象构造时,它会初始化mInitializer并保证流可以使用。
C++11通过假设每个#include
在C++03中,明显的意图(如脚注所示)是保证std::cin/std::cout对象在其他对象之前完全构造出来。