View in Telegram
​​std::cout Кажется, что на начальном этапе становления про-с++-ером, вывод в использование конструкции:
std::cout << "Print something in consol\n";
воспринимается, как "штука, которая выводит текст на консоль". Даже со временем картинка не до конца складывается и на вопрос "что такое std::cout?", многие плывут. Сегодня закроем этот вопрос. В этой строчке мы вызываем такой оператор:
std::ostream& operator<< (std::ostream& stream, const char * str)
Получается, что std::cout - объект класса std::ostream. И ни какой-то там временный. Раз он принимается по левой ссылке, значит он уже где-то хранится в памяти. Но мы же ничего не делаем для его создания? Откуда он взялся? Мы говорили о том, что есть "невидимые" для нас вещи, которые происходят при старте программы. Так вот, это одна из таких вещей. std::cout - глобальный объект типа std::ostream. За его создание отвечает класс std::ios_base::Init, инстанс которого явно или неявно определяется в библиотеке <iostream>. Но это все слова. И новичкам будет достаточно этого. Но мы тут глубоко закапываемся, поэтому давайте закопаемся в код. Полазаем по исходникам gcc. Ссылочки кликабельные для пытливых умов. А в хэдэре iostream мы можем найти вот это:
extern istream cin;  ///< Linked to standard input
extern ostream cout;  ///< Linked to standard output
extern ostream cerr;  ///< Linked to standard error (unbuffered)
extern ostream clog;  ///< Linked to standard error (buffered)
...
static ios_base::Init __ioinit;
Здесь определяются символы стандартных потоков и создается глобальная переменная класса ios_base::Init. Пойдемте тогда в конструктор:
ios_base::Init::Init()
  {
    if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)
      {
    // Standard streams default to synced with "C" operations.
    _S_synced_with_stdio = true;

    new (&buf_cout_sync) stdio_sync_filebuf<char>(stdout);
    new (&buf_cin_sync) stdio_sync_filebuf<char>(stdin);
    new (&buf_cerr_sync) stdio_sync_filebuf<char>(stderr);

    // The standard streams are constructed once only and never
    // destroyed.
    new (&cout) ostream(&buf_cout_sync);
    new (&cin) istream(&buf_cin_sync);
    new (&cerr) ostream(&buf_cerr_sync);
    new (&clog) ostream(&buf_cerr_sync);
    cin.tie(&cout);
    cerr.setf(ios_base::unitbuf);
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // 455. cerr::tie() and wcerr::tie() are overspecified.
    cerr.tie(&cout);
    ...
    __gnu_cxx::__atomic_add_dispatch(&_S_refcount, 1);
Немножко разберем происходящее. В условии проверяется ref_count, чтобы предотвратить повторную инициализацию. Так как не предполагается, что такие объекты, как cout будут удалены, они просто создаются через placement new с помощью инстансов stdio_sync_filebuf<char>. Это внутренний буфер для объектов потоков, который ассоциирован с "файлами" stdout, stdin, stderr. Буферы как раз и предназначены для получения/записи io данных. Хорошо. Мы видим как и где создаются объекты. Но это же placement new. Для объектов уже должная быть подготовлена память для их размещения. Где же она? В файлике globals_io.cc:
 // Standard stream objects.
  // NB: Iff <iostream> is included, these definitions become wonky.
  typedef char fake_istream[sizeof(istream)]
  attribute ((aligned(alignof(istream))));
  typedef char fake_ostream[sizeof(ostream)]
  attribute ((aligned(alignof(ostream))));
  fake_istream cin;
  fake_ostream cout;
  fake_ostream cerr;
  fake_ostream clog;
то есть, объекты - это пустые символьные массивы правильного размера и выравнивания. Все это должно вам дать довольно полное представление, что такое стандартные потоки ввода-вывода. #cppcore #compiler
Love Center
Love Center
Find friends or serious relationships easily