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的实例.请参阅此演示code snippet:

#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上的这个link似乎不是很有意义.它没有清楚地告诉我std::cout是如何实现的,以及std::ostream的构造函数是如何创建std::cout的.据我所知,最相关的信息是:

全局对象std::coutstd::wcout控制到与标准C输出流stdout相关联的实现定义类型(从std::streambuf派生)的流缓冲器的输出.

没什么了.

我正在用gcc 4.9Ubuntu

感谢@NathanPierson.

他告诉我的

std::basic_ostream有一个构造函数,它接受指向std::basic_streambuf对象的指针.std::cout使用指向某个实现定义的派生类std::basic_streambuf的实例的指针进行初始化.

,这让我更接近答案.

推荐答案

如何创建std::cout?

第一件事,从https://en.cppreference.com/w/cpp/io/ios_base/Init开始:

std::ios\U base::Init

此类用于确保默认的C++流(std::cin,

The header behaves as if it defines (directly or indirectly) an instance of std::ios\U base::Init with static storage duration: [...]

Meh,让我们做一个真实的代码示例.我将使用GCC C++ library.从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类的contsructor,在https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/ios_init.cc#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,在这里实现https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/ext/stdio_sync_filebuf.h#L56.它继承自std::basic_streambuf.

So coutstdio_sync_filebuf<char>为参数构造in-place.这是这里提到的第一个构造函数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个例子.

Linux相关问答推荐

AddressSaniizer随机抛出没有任何解释的SIGSEGV

为什么硬编码的阿拉伯字母与Unicode代码点不具有相同的值

在新环境中使用Unicode范围的sed表达式有问题

如何将数据从Linux内核中的块驱动程序持久存储到存储中?

AWK+向AWK导出值未传递

无法分析nasm中的单词

Git - 打印以不同编码混合的文件

如何让xargs对 bash 脚本中find命令找到的所有文件执行?

如何在充满 csv 的目录中获得不同的值计数

在 bash 中查找匹配多个模式的文件

如何在 Linux 中向应用程序发出信号而不杀死它?

yum 可以告诉我哪些存储库提供了特定的包吗?

有没有办法在整个项目代码中的某个日期之后找出更改的文件?

Linux上C中的标准输出线程安全?

在 Python 中删除 Root 权限

初学者如何在 Linux 中开始使用 Mono?

/dev/random 非常慢?

获取本地时区的 Olson TZ 名称?

ImportError:在 ubuntu 14.04 中没有名为 _io 的模块

如何通过命令行识别特定的 Linux 风格?