4

在这里,您会在哪个标题下找到以下语句?:

最后,<iostream>提供了八个标准的全局对象(cin、cout 等)。要正确执行此操作,此标头还提供<istream><ostream>标头的内容,但仅此而已。这个标题的内容看起来像

#include <ostream>
#include <istream>

namespace std
{
    extern istream cin;
    extern ostream cout;
    ....

    // this is explained below
    static ios_base::Init __foo;    // not its real name
}

现在,前面提到的运行时惩罚:必须在您自己的任何代码使用它们之前初始化全局对象;这是由标准保证的。像任何其他全局对象一样,它们必须被初始化一次且仅一次。这通常通过类似上述的构造来完成,并且出于这个原因,标准中指定了嵌套类 ios_base::Init。

它是如何工作的?因为标头包含在您的任何代码之前,所以 __foo 对象在您的任何对象之前构造。(全局对象按照它们声明的顺序构建,并以相反的顺序销毁。)构造函数第一次运行时,设置了八个流对象。

<iostream>我的问题:当我在多个文件中包含头文件时.cpp,上面的方案如何保证对象、等只有一个定义?cincout

4

1 回答 1

3

它并不能真正保证这一点。通过在作为库一部分的 .cpp 文件中简单地定义一次流对象来解决一个定义问题。问题中的代码仅包含标准流的声明

可以保证的是对象在使用之前会被初始化。C++ 中全局对象的一个​​问题是,虽然它们在每个 .cpp 文件中按顺序初始化,但我们不知道链接器将对象从不同文件中放入的顺序。如果一个对象在初始化之前尝试使用另一个对象,并且我们不知道确切的顺序,这可能会导致问题 - 请参阅静态初始化顺序惨败

此处使用的一种解决方法是将Init对象放在声明流对象的标头中。因为您必须在使用流之前包含标头,所以我们知道该Init对象将位于文件的顶部,因此在可能使用流的其他对象之前构造。

现在,Init类的构造函数负责流对象的初始化。这必须尽早完成,而且只需要一次。具体如何由每个实现决定,但代码提示使用计数器来跟踪Init创建的对象的数量(并且可能会特别处理第一个对象)。

这只是执行此操作的一种方式,仅使用标准语言。一些实现还有其他技巧,例如#pragmainit_priority指令来说服链接器将库代码放在用户代码之前。在这种情况下,它只是通过魔法工作,而不使用Init类。

于 2013-02-16T12:44:39.287 回答