当前位置: 首页 > 知识库问答 >
问题:

如何实现“std::cout”?

谯振国
2023-03-14

std::cout是std::ostream的一个实例。我可以在一个名为usr/include/c/7/iostream的文件中看到std::cout的声明

extern ostream cout;      /// Linked to standard output

而std::ostream由typedef std::basic\u 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

CppReference上的这个链接似乎没有什么意义。它没有清楚地告诉我如何实现std::cout,以及std::ostream的构造函数如何创建std::cout。据我所知,最相关的信息是:

全局对象控制到与标准C输出流关联的实现定义类型的流缓冲区的输出(派生自标准C输出流)。

没什么了。

我正在使用gcc 4.9开发Ubuntu

感谢@NathanPierson。

他告诉我的

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

,这让我更接近答案。


共有2个答案

桂高昂
2023-03-14

编译器及其标准库实现可以使用程序员无法使用的非标准特性进行协作。

这在这种情况下是不必要的,因为有一个相当标准的公共构造函数:

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

如果您准备好了stream buf,您可以创建ostream类型的对象,标准库也是如此。

streambuf实际上是一个隐藏的实现细节,但在典型的实现中,它可能是由stdout(C样式)构造的自定义类的对象

邓开济
2023-03-14

std::c是如何创建的?

首先,从https://en.cppreference.com/w/cpp/io/ios_base/Init开始:

d::ios_base::初始化

此类用于确保正确初始化和销毁默认C流(std::cin、std::cout等)。[...]

标头的行为就好像它定义了(直接或间接)std::ios\u base::Init的实例,静态存储持续时间:[…]

Meh,让我们做一个真实的代码示例。我将使用GCC C库。从…起https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/std/iostream#L73,这是重要的部分:

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

现在我们跳到ios\u base::Init类的contsructor,在https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/src/c++98/ios\u 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);

当您调用ios\u base::Init::Init()时,S\u refcount就在那里 从静态类的构造函数手动执行,它可以防止双重初始化。

stdio\u sync\u filebuf是一个内部缓冲区,用于存储输入/输出数据,在这里实现https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/ext/stdio\u sync\u filebuf。h#L56。它继承自std::basic\u streambuf。

因此,可以使用stdio\u sync\u filebuf就地构建

现在,因为这些东西是就地html" target="_blank">构建的,您可能想知道内存是如何分配的?从…起https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/src/c++98/globals\u io。抄送#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;

对象只是空的缓冲区,大小和对齐方式都合适。

是的,您可以使用GCC上的stdio\u sync\u filebuf构建ostream:

#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::stream buf的类,并自己构建ostream。网上有很多例子,比如这里https://stackoverflow.com/a/51250032/9072753。

 类似资料: