zoukankan      html  css  js  c++  java
  • C++标准库中的std::endl究竟做了什么?

    先抓出std::endl的源代码:

    /**

     *  @file  ostream

     *  @brief  Write a newline and flush the stream.

     *

     *  This manipulator is often mistakenly used when a simple newline is

     *  desired, leading to poor buffering performance.  See

     *  http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt11ch25s02.html

     *  for more on this subject.

    */

    template<typename _CharT, typename _Traits>

    inline basic_ostream<_CharT, _Traits>&

    endl(basic_ostream<_CharT, _Traits>& __os)

    {

      return flush(__os.put(__os.widen(' ')));

    }

    可以看到endl实际是名字空间std中的一个全局内联函数记住std::endl是一个函数),它做了两件事:

    1) 输出一个换行符(为何要输出一个换行符,如果不输出会怎么样?);

    2) 调用flush

    继续看flush这个函数(也是std名字空间内的全局内联函数):

    /**

     *  @brief  Flushes the output stream.

     *

     *  This manipulator simply calls the stream's @c flush() member function.

    */

    template<typename _CharT, typename _Traits>

    inline basic_ostream<_CharT, _Traits>&

    flush(basic_ostream<_CharT, _Traits>& __os)

    {

      return __os.flush(); // 注意这里的os不是操作系统的意思,而是类basic_ostream的缩写

    }

    进一步查看std::basic_ostream::flush的代码:

    /**

     *  @file  ostream.tcc

    */

    template<typename _CharT, typename _Traits>

    basic_ostream<_CharT, _Traits>&

    basic_ostream<_CharT, _Traits>::flush()

    {

      // _GLIBCXX_RESOLVE_LIB_DEFECTS

      // DR 60. What is a formatted input function?

      // basic_ostream::flush() is *not* an unformatted output function.

      ios_base::iostate __err = ios_base::goodbit;

      __try

      {

        // 刷新发生在pubsync,底层调用的是LIBC库函数fflush

        if (this->rdbuf() && this->rdbuf()->pubsync() == -1)

          __err |= ios_base::badbit;

      }

      __catch(__cxxabiv1::__forced_unwind&)

      {

        this->_M_setstate(ios_base::badbit);

        __throw_exception_again;

      }

      __catch(...)

      {

        this->_M_setstate(ios_base::badbit);

      }

      if (__err)

        this->setstate(__err);

      return *this;

    }

    下段小代码,可以看到两个帮助释疑的调用栈,:

    $ cat xxx.cpp

    #include <iostream>

    int main() {

      std::cout << std::endl;

      return 0;

    }

    写换符符:

    #0  0x00007ffff72e0840 in write () from /lib64/libc.so.6

    #1  0x00007ffff726cfb3 in _IO_new_file_write () from /lib64/libc.so.6

    #2  0x00007ffff726e41c in __GI__IO_do_write () from /lib64/libc.so.6

    #3  0x00007ffff726e7f3 in __GI__IO_file_overflow () from /lib64/libc.so.6

    #4  0x00007ffff726ac49 in putc () from /lib64/libc.so.6

    #5  0x00007ffff7b694c6 in std::ostream::put(char) () from /lib64/libstdc++.so.6

    #6  0x00007ffff7b69712 in std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) ()

       from /lib64/libstdc++.so.6

    #7  0x0000000000400803 in main () at xxx.cpp:3

    刷新流:

    #0  0x00007ffff7262030 in fflush () from /lib64/libc.so.6

    #1  0x00007ffff7b68d5e in std::ostream::flush() () from /lib64/libstdc++.so.6

    #2  0x0000000000400803 in main () at xxx.cpp:3

    实际上,还可以看到更多,如全局对象std::coutstd::cerrstd::clog等的析构:

    #0  0x00007ffff7262030 in fflush () from /lib64/libc.so.6

    #1  0x00007ffff7b68d5e in std::ostream::flush() () from /lib64/libstdc++.so.6

    #2  0x00007ffff7b41bc8 in std::ios_base::Init::~Init() () from /lib64/libstdc++.so.6

    #3  0x00007ffff722fa69 in __run_exit_handlers () from /lib64/libc.so.6

    #4  0x00007ffff722fab5 in exit () from /lib64/libc.so.6

    #5  0x00007ffff7218c0c in __libc_start_main () from /lib64/libc.so.6

    #6  0x0000000000400729 in _start ()

    Init析构的同时析构cout等:

    /**

     *  @file  ios_init.cc

    */

    ios_base::Init::~Init()

    {

      // Be race-detector-friendly.  For more info see bits/c++config.

      _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_S_refcount);

      if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, -1) == 2)

      {

        _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_S_refcount);

        // Catch any exceptions thrown by basic_ostream::flush()

        __try

        {

          // Flush standard output streams as required by 27.4.2.1.6

          cout.flush();

          cerr.flush();

          clog.flush();

        

      #ifdef _GLIBCXX_USE_WCHAR_T

          wcout.flush();

          wcerr.flush();

          wclog.flush();

      #endif

        }

        __catch(...)

        {

        }

      }

    }

    文件ios_init.cc其它相关函数源码:

    1) std::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);

    #ifdef _GLIBCXX_USE_WCHAR_T

        new (&buf_wcout_sync) stdio_sync_filebuf<wchar_t>(stdout);

        new (&buf_wcin_sync) stdio_sync_filebuf<wchar_t>(stdin);

        new (&buf_wcerr_sync) stdio_sync_filebuf<wchar_t>(stderr);

        new (&wcout) wostream(&buf_wcout_sync);

        new (&wcin) wistream(&buf_wcin_sync);

        new (&wcerr) wostream(&buf_wcerr_sync);

        new (&wclog) wostream(&buf_wcerr_sync);

        wcin.tie(&wcout);

        wcerr.setf(ios_base::unitbuf);

        wcerr.tie(&wcout);

    #endif

        // NB: Have to set refcount above one, so that standard

        // streams are not re-initialized with uses of ios_base::Init

        // besides <iostream> static object, ie just using <ios> with

        // ios_base::Init objects.

        __gnu_cxx::__atomic_add_dispatch(&_S_refcount, 1);

      }

    }

    2) 全局函数ios_base::sync_with_stdio

    bool

    ios_base::sync_with_stdio(bool __sync)

    {

      // _GLIBCXX_RESOLVE_LIB_DEFECTS

      // 49.  Underspecification of ios_base::sync_with_stdio

      bool __ret = ios_base::Init::_S_synced_with_stdio;

      // Turn off sync with C FILE* for cin, cout, cerr, clog iff

      // currently synchronized.

      if (!__sync && __ret)

      {

        // Make sure the standard streams are constructed.

        ios_base::Init __init;

        ios_base::Init::_S_synced_with_stdio = __sync;

        // Explicitly call dtors to free any memory that is

        // dynamically allocated by filebuf ctor or member functions,

        // but don't deallocate all memory by calling operator delete.

        buf_cout_sync.~stdio_sync_filebuf<char>();

        buf_cin_sync.~stdio_sync_filebuf<char>();

        buf_cerr_sync.~stdio_sync_filebuf<char>();

    #ifdef _GLIBCXX_USE_WCHAR_T

        buf_wcout_sync.~stdio_sync_filebuf<wchar_t>();

        buf_wcin_sync.~stdio_sync_filebuf<wchar_t>();

        buf_wcerr_sync.~stdio_sync_filebuf<wchar_t>();

    #endif

        // Create stream buffers for the standard streams and use

        // those buffers without destroying and recreating the

        // streams.

        new (&buf_cout) stdio_filebuf<char>(stdout, ios_base::out);

        new (&buf_cin) stdio_filebuf<char>(stdin, ios_base::in);

        new (&buf_cerr) stdio_filebuf<char>(stderr, ios_base::out);

        cout.rdbuf(&buf_cout);

        cin.rdbuf(&buf_cin);

        cerr.rdbuf(&buf_cerr);

        clog.rdbuf(&buf_cerr);

    #ifdef _GLIBCXX_USE_WCHAR_T

      new (&buf_wcout) stdio_filebuf<wchar_t>(stdout, ios_base::out);

      new (&buf_wcin) stdio_filebuf<wchar_t>(stdin, ios_base::in);

      new (&buf_wcerr) stdio_filebuf<wchar_t>(stderr, ios_base::out);

      wcout.rdbuf(&buf_wcout);

      wcin.rdbuf(&buf_wcin);

      wcerr.rdbuf(&buf_wcerr);

      wclog.rdbuf(&buf_wcerr);

    #endif

      }

      return __ret;

    }

    前面提到的endl为何要输出一个换行符,这是因为fflush只是将数据刷到标准输出,标准输出本身也是有缓存的,而且默认是行缓存_IOLBF line buffered)。

    是否可将标准输出设置为全缓存_IOFBF fully buffered)了?答案是可以,执行下列代码即可得到结果。

    $ cat eee.cpp

    #include <stdio.h>

    int main(void) {

      char buf[BUFSIZ];

      setvbuf(stdout, buf, _IOFBF, BUFSIZ);

      printf("Hello, world! ");

      getchar();

      return 0;

    }

    是否行缓存和全缓存,与是否为字符设备(Character Device,只能顺序读取)或块设备(Block Device,支持随机存取)无关。

    将标准输出定向到普通文件,则缓存类型同普通文件,即默认全缓存:

    $ cat eee.cpp

    #include <fcntl.h>

    #include <stdio.h>

    #include <stdlib.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <unistd.h>

    int main(int argc, char* argv[]) {

      char buf[BUFSIZ];

      int fd = open("/tmp/xxx.txt", O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);

      if (fd == -1) {

        perror("open");

        return 1;

      } else {

        int fdnew = dup2(fd, STDOUT_FILENO); // 让标准输出指向文件

        if (fdnew == -1) {

          perror("dup2");

          return 1;

        } else {

          printf("123 ");

          getchar();

          return 0;

        }

      }

    }

    匿名管道默认是全缓存,可用下列代码验证:

    $ cat xxx.cpp

    #include <fcntl.h>

    #include <stdio.h>

    #include <stdlib.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <sys/wait.h>

    #include <unistd.h>

    int main(int argc, char* argv[]) {

      int fd[2];

      char buf[BUFSIZ];

      if (pipe(fd) == -1) {

        perror("pipe");

        exit(1);

      }

      pid_t pid = fork();

      if (pid == -1) {

        perror("fork");

        exit(1);

      } else if (pid == 0) { // Child process

        dup2(fd[1], STDOUT_FILENO);

        //setvbuf(stdout, buf, _IOLBF, BUFSIZ);

        printf("hello ");

        getchar();

        fflush(stdout);

        _exit(0);

      } else { // Parent process

        char msg[BUFSIZ];

        int n = read(fd[0], msg, sizeof(msg)-1);

        if (n == -1) {

          perror("Parent read");

        } else {

          msg[n] = '';

          fprintf(stdout, "(%d)%s ", n, msg);

        }

        

        wait(NULL);

        exit(0);

      }

    }

    有名管道和套接字(包含UNIX套接字也是全缓存),附问题:如何在GDB中调试跟踪std::cout

  • 相关阅读:
    [转]用mamcache 存储session的好处
    [转]怎么写 JQuery插件 (案例原理)
    关于查询优化
    HBase Canary
    HBase Bulk Loading
    有用的技术网站
    HBase 运维分析
    HBase rest
    hbase mlockall
    Jamon
  • 原文地址:https://www.cnblogs.com/aquester/p/11850495.html
Copyright © 2011-2022 走看看