哪些文件必须包含在 iostream 中

What files must iostream be included in?

本文关键字:iostream 包含 文件      更新时间:2023-10-16

我正在编写一个快速示例来演示全局对象的初始化/销毁。 在这样做的过程中,我遇到了以下难题。

一般而言,不应在头文件中包含头文件中代码不需要的任何头。 这有助于减少混淆和编译时间。

在我的示例中,我将构造函数和析构函数的定义从类定义中分离出来。 就我而言,它们是如此微不足道,以至于我可能只是内联它们,但这只是一个例子。

实际定义构造函数和析构函数的转换单元包括iostream,以便它可以将调用输出到控制台。

我的

问题是,当我们开始讨论在全局范围内声明我的类的实例时。 现在我遇到了初始化顺序和翻译单元。 翻译单元中全局变量的初始化顺序已明确定义。 全局变量从一个翻译单元到另一个转换单元的初始化顺序不太明确。 在我的示例中,它可以执行以下操作之一:

    初始化第一个源文件
  • ,然后初始化第二个源文件。
  • 初始化第二个源文件
  • ,然后初始化第一个源文件。
  • 初始化第二个源文件。

24.4.1/2:对象 [std::cout 在我的例子中] 是在构造类 ios_base::Init 的对象之前或期间的某个时间构建的,并且在任何情况下都是在 main 主体开始执行之前构建的。在程序执行期间不会销毁对象。在翻译单元中包含<iostream>的结果应<iostream>定义具有静态存储持续时间的ios_base::Init实例。同样,整个程序的行为就好像至少有一个具有静态存储持续时间的ios_base::Init实例一样。

请注意,此段落调用了 like 规则,因此它不必创建 ios_base::Init 的实例,但它的行为必须像它一样。

假设编译器和标准库确实按照段落中指定的行为,并且不做不同但等效的事情,似乎对我的程序唯一有效的初始化顺序是初始化第一个源文件,然后初始化第二个源文件。 否则,在尝试使用它之前,std::cout 将不会初始化。

头文件:

#ifndef HEADER_H_
#define HEADER_H_
struct A {
  A(const char*);
  ~A();
  const char* v;
}
#endif

第一个源文件:

#include "header.h"
#include <iostream>
A::A(const char* val) : v{ val } {
  std::cout << v << "n";
}
~A() {
  std::cout << "~" << v << "n";
}
A a{ "a" };
A b{ "b" };

第二个源文件:

#include "header.h"
A c{ "c" };
A d{ "d" };
int main() {
}

在写这个问题时,我意识到答案包含在 3.6.2/4 中。

是否在

main 的第一个语句之前完成具有静态存储持续时间的非局部变量的动态初始化是实现定义的。如果初始化推迟到第一个 main 语句之后的某个时间点,则应在第一次使用与要初始化的变量相同的翻译单元中定义的任何函数或变量之前进行初始化。

这意味着即使它决定只初始化第二个源文件,对 c 构造函数的调用也会导致第一个源文件在实际执行 c 的构造函数之前被初始化。 这保证了静态初始化完成的顺序为:

  1. ios_base::Init
  2. a
  3. b
  4. c
  5. d

由于初始化顺序没有歧义,因此没有问题。