簡體   English   中英

`std::cout` 是如何實現的?

[英]How is `std::cout` implemented?

std::coutstd::ostream的一個實例。 我可以在名為/usr/include/c++/7/iostream的文件中看到std::cout的聲明:

extern ostream cout;      /// Linked to standard output

std::ostreamtypedef std::basic_ostream<char> std::ostream定義。

而且,您似乎無法創建std::ostream的實例。 請參閱此演示代碼片段

#include<iostream>

int main()
{
    std::ostream os;
    return 0;
}

以下是編譯器對上面代碼片段的抱怨:

In file included from /opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/iostream:39:0,
                 from <source>:1:
/opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/ostream: In function 'int main()':
/opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/ostream:384:7: error: 'std::basic_ostream<_CharT, _Traits>::basic_ostream() [with _CharT = char; _Traits = std::char_traits<char>]' is protected
       basic_ostream()
       ^
<source>:5:18: error: within this context
     std::ostream os;
                  ^

問題出現了,因為std::basic_ostream<_CharT, _Traits>::basic_ostream()被標記為受保護, std::cout是如何創建的?

CppReference 上的這個鏈接似乎意義不大。 它沒有清楚地告訴我std::cout是如何實現的,以及std::cout是如何由std::ostream的構造函數創建的。 據我所知,最相關的信息是:

全局對象std::coutstd::wcout控制 output 到實現定義類型的 stream 緩沖區(派生自std::streambuf ),與標准 C output stream stdout關聯。

僅此而已。

我正在使用Ubuntugcc 4.9

感謝@NathanPierson。

他告訴我

std::basic_ostream有一個構造函數,該構造函數采用指向std::basic_streambuf object 的指針。std std::cout使用指向std::basic_streambuf的某個實現定義的派生 class 的實例的指針進行初始化。

,這讓我離答案更近了。

std::cout 是如何創建的?

首先,來自https://en.cppreference.com/w/cpp/io/ios_base/Init

std::ios_base::Init

此類用於確保默認 C++ 流(std::cin、std::cout 等)正確初始化和銷毀​​。 [...]

標頭<iostream>的行為就好像它(直接或間接)定義了一個具有靜態存儲持續時間的 std::ios_base::Init 實例:[...]

嗯,讓我們做一個真實的代碼示例。 我將使用GCC C++ 庫 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/iostream#L73 ,這是重要的部分:

 // For construction of filebuffers for cout, cin, cerr, clog et. al.
 static ios_base::Init __ioinit;

現在我們跳轉到ios_base::Init類的構造函數,在https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/ios_init。抄送#L85

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);

_S_refcount用於您何時調用ios_base::Init::Init(); 從靜態類的構造函數手動,它可以防止雙重初始化。

stdio_sync_filebufistream / ostream的內部緩沖區,它旨在處理cstdio FILE*操作以獲取/放置輸入/輸出數據,在此處實現https://github.com/gcc-mirror/gcc/blob/master/ libstdc%2B%2B-v3/include/ext/stdio_sync_filebuf.h#L56 它繼承自std::basic_streambuf

所以cout是用stdio_sync_filebuf<char>作為參數就地構造的 這是這里提到的第一個構造函數https://en.cppreference.com/w/cpp/io/basic_ostream/basic_ostream

現在,因為這些東西是就地構建的,你可能想知道內存是如何分配的? https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/globals_io.cc#L50

  // 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;

這些對象只是適當大小和適當對齊的空char緩沖區。

是的,您可以自己構建 ostream,在 GCC 上使用__gnu_cxx::stdio_sync_filebuf

#include <fstream>
#include <ext/stdio_sync_filebuf.h>
int main() {
    __gnu_cxx::stdio_sync_filebuf<char> mybuf_cout_sync(stdout);
    std::ostream os(&mybuf_cout_sync);
    os << "Hello world!\n";
    return 0;
}

或者,為了便於移植,您可以編寫自己的類,該類繼承自std::streambuf並自己從中構造ostream 網上有很多例子,例如這里https://stackoverflow.com/a/51250032/9072753

編譯器及其標准庫實現可以使用非標准功能進行協作,這些功能僅由程序員使用。

在這種情況下這不是必需的,因為有一個非常標准的公共構造函數:

explicit basic_ostream(basic_streambuf<char_type, Traits>* sb);

如果您准備好streambuf ,則可以創建ostream類型的對象,標准庫也可以。

那個streambuf究竟是一個隱藏的實現細節,但在典型的實現中,它可能是從stdout (C風格的<cstdio>文件指針)構造的自定義類的對象。

我認為當前答案的一部分缺失,以及您的問題的一部分:
std::cout這個名字也是“神奇的”。
這意味着標准庫知道它,並為終端提供特定於操作系統的必要連接; 使用相應的(和特定於操作系統的)系統調用進行輸出等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM