std::cout是如何实现的?protected构造下它如何被实例化?
std::cout是std::ostream的实例,在GCC 7的头文件/usr/include/c++/7/iostream中可以看到它的声明:
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; }
在GCC 4.9环境下编译上述代码,会抛出如下错误:
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的无参构造函数被标记为protected,用户代码没有权限调用,这就引出了核心疑问:标准库是如何创建std::cout实例的?
查阅公开的标准库文档,仅能找到关于std::cout的高层行为描述,没有说明具体实现逻辑:
全局对象
std::cout和std::wcout控制输出到实现定义类型的流缓冲区(派生自std::streambuf),该缓冲区与标准C输出流stdout关联。
实现原理
这里的认知误区很容易被点破:受保护的仅仅是std::basic_ostream的无参构造函数,这个类还提供了公有的带参构造函数。std::basic_ostream有一个公有构造函数,接收std::basic_streambuf类型的指针作为参数。标准库初始化std::cout时,根本不会调用那个受保护的无参构造:
- 首先在标准库内部实现中,会先构造一个派生自
std::basic_streambuf的自定义流缓冲区实例,这个缓冲区的内部逻辑封装了和C标准流stdout的交互,属于实现定义的内部类型,不对外暴露。 - 之后将这个内部流缓冲区实例的指针作为参数,调用
std::basic_ostream的公有构造函数,就完成了std::cout的初始化。
用户代码中直接写std::ostream os;会报错,是因为这个写法默认调用无参构造函数,而这个无参构造的设计初衷就是给标准库内部的派生流类(比如同时支持读写的std::iostream)使用的,普通用户本来就不应该创建没有绑定任何底层缓冲区的空std::ostream对象,所以被设计为protected权限完全合理。
注:上述分析思路由Nathan Pierson提供。
内容的提问来源于stack exchange,提问作者John

