[英]How is `std::cout` implemented?
std::cout
是std::ostream
的一個實例。 我可以在名為/usr/include/c++/7/iostream
的文件中看到std::cout
的聲明:
extern ostream cout; /// Linked to standard output
std::ostream
由typedef 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::cout
和std::wcout
控制 output 到實現定義類型的 stream 緩沖區(派生自std::streambuf
),與標准 C output streamstdout
關聯。
僅此而已。
我正在使用Ubuntu
和gcc 4.9
感謝@NathanPierson。
他告訴我
std::basic_ostream
有一個構造函數,該構造函數采用指向std::basic_streambuf
object 的指針。stdstd::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_filebuf
是istream
/ 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.