关于 std::cout,为啥使用“外部”而不是“单例模式”

Posted

技术标签:

【中文标题】关于 std::cout,为啥使用“外部”而不是“单例模式”【英文标题】:About std::cout, why use "extern" rether than "singleton pattern"关于 std::cout,为什么使用“外部”而不是“单例模式” 【发布时间】:2019-07-20 08:22:01 【问题描述】:

我读了有效的C++第04项提到的

通过用本地静态对象替换非本地静态对象来避免跨翻译单元的初始化顺序问题。

我认为“全局且唯一的对象”应该是单例模式,而不是我阅读此项目后的外部对象。

比如I/O对象(std::cout)

但 std::cout 似乎是外部对象。 (http://www.cplusplus.com/reference/iostream/cout/)

我对此感到困惑。

编辑:添加代码

我从这本书中捕获了一些代码。

首先是错误代码:

class FileSystem 
// from your library’s header file
public:
 ...std::size_t numDisks( ) const;
// one of many member functions...
; 
extern FileSystem tfs;

在不同翻译单元中定义的非局部静态对象的初始化相对顺序未定义。

所以当我调用 tfs 时,上述代码可能有问题。

因为 tfs 可能无法完成初始化。

推荐代码:

class FileSystem  ... ; // as before
FileSystem& tfs()

  static FileSystem fs;
  return fs;

class Directory  ... ;// as beforeDirectory::Directory( params )
Directory::Directory( params ) // as before, except references to tfs are 
                               //now to tfs( )

...
std::size_t disks = tfs().numDisks( );
...

Directory& tempDir()

  static Directory td(params);
  return td;

【问题讨论】:

因为 C++ 是根据 C 建模的,它具有全局 stdin/stdout/stderr 变量。 既然可以使用“extern”,为什么还要使用“singleton” 顺便说一句,std::cout (几乎)不会发生初始化顺序问题。 相关阅读:***.com/questions/8784892/…en.cppreference.com/w/cpp/io/ios_base/Init 【参考方案1】:

使用extern 变量允许(主观上)更好的语法

std::cout << stuff;

强调标准流是一个独特的对象,而不是某些函数调用的结果。由于流式传输意味着要在流对象中完成,因此使用对象表示法可以更好地结合它。

至于静态初始化顺序惨败,标准库通过使用Schwarz Counter 技术进行初始化来避免它。初始化以明确定义的顺序发生,因为包含 iostreamInit 类型的特殊全局对象添加到包含 TU。对象的构造函数在第一次使用之前处理流的初始化,无论第一次使用在哪里。

【讨论】:

谢谢!关键字看起来像“Schwarz Counter”【参考方案2】:

因为 C++ 选择了更精细的方法,获得了直接对象访问的效率和简单性,并且仍然避免了静态初始化顺序的失败。

怎么做?

c++ 标准流实际上并不是静态初始化的。相反,#include-ing &lt;iostream&gt; 定义了一个类型为 std::ios_base::Init 的全局静态对象,它管理全局引用计数,以及 C++ 流的初始化。

或者至少,它是这样指定的,但总是有as-if rule。

【讨论】:

以上是关于关于 std::cout,为啥使用“外部”而不是“单例模式”的主要内容,如果未能解决你的问题,请参考以下文章

为啥 printf() 可以在内核中工作,但使用 std::cout 不能?

为啥 std::cout 输出溢出?

为啥重新声明 std::cout 会导致分段错误?

为啥我们需要绑定 std::cin 和 std::cout?

按位运算的意外结果为啥 (1 | 1 & 2) 给出 1 而不是 2?

R 检查不喜欢 std:cout (C++)