В какие файлы должен быть включен iostream?

0

Я писал быстрый пример, чтобы продемонстрировать инициализацию/уничтожение глобальных объектов. При этом я натолкнулся на следующую загадку.

Вообще говоря, вы не должны включать заголовки в заголовочный файл, которые не требуются для кода в файле заголовка. Это помогает уменьшить путаницу и время компиляции.

В моем примере я отделил определение конструктора и деструктора от определения класса. В моем случае они настолько тривиальны, что я, вероятно, просто встраиваю их, но это для примера.

Блок перевода, который фактически определяет конструктор и деструктор, включает iostream чтобы он мог выводить вызовы на консоль.

Там, где мой вопрос возникает, мы начинаем говорить об объявлении экземпляров моего класса в глобальной области. Теперь я запускаю порядок инициализации и единицы перевода. Порядок инициализации глобальных переменных внутри единицы перевода хорошо определен. Порядок инициализации глобальных переменных из одной единицы перевода в другую менее точно определен. В моем примере он может выполнить одно из следующих действий:

  • Инициализируйте первый исходный файл, затем второй исходный файл.
  • Инициализируйте второй исходный файл, затем первый исходный файл.
  • Инициализируйте второй исходный файл.

24.4.1/2: Объекты [std :: cout в моем случае] построены и ассоциации устанавливаются в некоторый момент до или в первый раз, когда объект класса ios_base::Init построен и в любом случае до тело главного начинает исполнение. Во время выполнения программы объекты не уничтожаются. Результаты включения <iostream> в блок трансляции должны быть такими, как если бы <iostream> определял экземпляр ios_base::Init со статической продолжительностью хранения. Точно так же вся программа должна вести себя так, как если бы был хотя бы один экземпляр ios_base::Init со статической продолжительностью хранения.

Обратите внимание, что этот абзац вызывает правило as if, поэтому ему не нужно создавать экземпляр 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() {
}
  • 1
    TL; DR; Вкратце: вводите <iostream> всякий раз, когда вы используете что-то из объявлений оттуда.
Теги:
c++11

1 ответ

0

При написании этого вопроса я понял, что ответ содержится в 3.6.2/4.

Реализация - это определение того, выполняется ли динамическая инициализация нелокальной переменной со статической продолжительностью хранения до первого утверждения main. Если инициализация отложена до некоторого момента времени после первой основной записи, она должна произойти до первого нечетного использования любой функции или переменной, определенной в той же самой единицы перевода, что и переменная, подлежащая инициализации.

Это означает, что даже если он решает просто инициализировать второй исходный файл, вызов конструктора c приведет к инициализации первого исходного файла до того, как он фактически выполнит конструктор для c. Это гарантирует, что порядок завершения статической инициализации:

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

Поскольку нет никакой двусмысленности для порядка инициализации, нет никаких проблем.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню