cout源码浅析
目录
cout源码浅析
那么对于没有定义在这之中的要怎么办呢?
实际使用
结语
首先来看我从cplusplus中截取的这张图:

注意最下面这一行字。cout其实是ostream的一个标准对象object。而上面则演示了一些继承关系。
好的,理解了之后,接下来就去观察一下源码实现吧!
cout源码浅析
测试环境:VS2019
对于初学C++的时候,我们常常直接这样去用:
#include <iostream>
using namespace std;
int main()
{cout << "Hello World" << endl;
}
那么理解了cout是一个object之后,我们就去查看一下它具体的实现:
在iostream头文件中,可以看到如下语句:

而该语句是包含在namespace std中的。
从这一句也可以看到cout的类型为ostream。
进一步查看前面的_CRTDATA2_IMPORT,可以看到宏定义:
#define _CRTDATA2_IMPORT _CRTIMP2_IMPORT
接着看:
#define _CRTIMP2_IMPORT __declspec(dllimport)
这里的__declspec是MSVC编译器的关键字, __declspec(dllimport)就表示这个东西是从别的DLL导入的。
这里多提一句extern(参考C++primer第5版41页):
为了支持分离式编译(separate compilation)机制,C++将声明和定义区分开来。
变量声明规定了变量的类型和名字,在这一点上定义与之相同。但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量:
extern int i; // 声明i而非定义i
int j; // 声明并定义j
任何包含了显示初始化的声明即可成为定义。我们能给extern关键字标记的变量赋一个初始值,但是这么做也就抵消了extern的作用。extern语句如果包含初始值就不再是声明,而变成定义了:
extern double pi = 3.1416; // 定义
在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。
变量能且只能被定义一次,但是可以被多次声明。
再来看cplusplus给出的cout注解:

着重注意这两句:
The object is declared in header<iostream>with external linkage and static duration: it lasts the entire duration of the program.
In terms ofstatic initialization order, cout is guaranteed to be properly constructed and initialized no later than the first time an object of typeios_base::Init is constructed, with the inclusion of<iostream>counting as at least one initialization of such objects with static duration.
也就是说,其初始化早在第一次构造 ios_base::Init 类型对象初始化的时候就完成了。而其声明在iostream中有,生命周期持续到整个程序结束。
好,那让我们继续往下分析ostream。可以在iosfwd中看到如下语句:

using语义在STL中应用非常广泛。事实上早期这些都是用typedef,C++2.0之后似乎鼓励用using。
可以发现,ostream类型实际上是basic_ostream,两个模板参数:一个char,一个char_traits.
traits,萃取机,其实是一种手法,充当中间层。把一些东西丢入萃取机,然后通过一些统一的接口,去得到相应的回答。
traits各式各样,有type traits、iterator traits、char traits、allocator traits、pointer traits、array traits
关于萃取机,推荐侯捷老师的《STL源码剖析》或侯老师的课程,讲的很好。
这里可以想象,char_trairts,反应的就是character的一些特性,可以通过这个萃取机去进行询问。比如是不是宽字符呐之类的。
接着往下,思考如何通过<<去把东西输出到屏幕上呢?其实就是通过操作符重载去根据逐个的类型实现,比如说针对int型的,代码如下:
basic_ostream& __CLR_OR_THIS_CALL operator<<(int _Val) { // insert an intios_base::iostate _State = ios_base::goodbit;const sentry _Ok(*this);if (_Ok) { // state okay, use facet to insertconst _Nput& _Nput_fac = _STD use_facet<_Nput>(this->getloc());ios_base::fmtflags _Bfl = this->flags() & ios_base::basefield;long _Tmp;if (_Bfl == ios_base::oct || _Bfl == ios_base::hex) {_Tmp = static_cast<long>(static_cast<unsigned int>(_Val));} else {_Tmp = static_cast<long>(_Val);}_TRY_IO_BEGINif (_Nput_fac.put(_Iter(_Myios::rdbuf()), *this, _Myios::fill(), _Tmp).failed()) {_State |= ios_base::badbit;}_CATCH_IO_END}_Myios::setstate(_State);return *this;}
至此,我们已经明白了cout的大致原理:
cout是一个ostream类型的对象,ostream其实是模板类basic_ostream,其内重载了针对各种各样的type的operator<<,然后通过返回自身,使得可以连续cout,例如cout << 1 << 2,cout << 1的返回类型仍然是*this,也就是cout,接下来便又会cout << 2
定义在basic_ostream中针对的类型有int、unsigned int等等等等.
那么对于没有定义在这之中的要怎么办呢?
回顾以前我们使用string的时候,不也能直接cout一个string类型吗?
观察string源码:

同样可以看到,一个string其实就是模板类basic_string,有对应的类型、萃取机、分配器。
而重载operator<<是在类外进行的操作:

到这里我们发现了差异:
在ostream中我们在类内重载了operator<<,其中参数为所需要针对的type,比如int。
而在string的实现中(想要cout一个string类型对象),我们在类外重载了operator<<,其带有两个参数:
basic_ostream<_Elem, _Traits>& _Ostr
与 const basic_string<_Elem, _Traits, _Alloc>& _Str
如果仅考虑cout而言,可以想象,前者为cout,后者为string本身。那么为何造成这样的差异呢?
参考C++primer第5版494页,重载输出运算符<<:
通常情况下,输出运算符的第一个形参是一个非常量ostream对象的引用。之所以ostream是非常量的是因为向流写入内容会改变其状态;而该形参是引用是因为我们无法直接复制一个ostream对象。
第二个形参一般来说是一个常量的引用,该常量是我们想要打印的类类型。第二个形参是引用的原因是我们希望避免复制形参;而之所以该形参可以是常量是因为(通常情况下)打印对象不会改变对象的内容。
为了与其他输出运算符保持一致,operator<<一般要返回它的ostream形参。
因此我们可以想见,在ostream类内重载了operator<<时,成员函数默认第一个形参是this指针,因此此时我们只需要带一个int(或是别的type)型的参数即可。而在类外则必须把第一个参数给显示地指明出来。
实际使用
比如这里我自定义类,类内成员三个int的abc,输出这个类的时候想一并输出a b c:
#include <iostream>
using namespace std;class MyData
{
public:int a, b, c;
public:MyData(int a, int b, int c) : a(a), b(b), c(c) {};
};ostream& operator << (ostream& os, const MyData& my_data)
{os << my_data.a << " " << my_data.b << " " << my_data.c;return os;
}int main()
{MyData Hbh(1, 2, 3);cout << Hbh << endl;return 0;
}
这里类外重载operator<<时我们严格遵守C++primer所述规则:两个参数都传引用,第二个形参为常量const,最后返回ostream形参。
但是这样在类外重载有一个弊端,就是为了类外去访问,我把类MyData的类内变量都定义为public的了。
但是在类内定义的时候,由于this指针并非ostream而是这个类本身,那么就会造成第一个参数非ostream对象,怎么办呢?办法如下:
#include <iostream>
using namespace std;class MyData
{int a, b, c;
public:MyData(int a, int b, int c) : a(a), b(b), c(c) {};friend ostream& operator << (ostream& os, const MyData& my_data){os << my_data.a << " " << my_data.b << " " << my_data.c;return os;}
};int main()
{MyData Hbh(1, 2, 3);cout << Hbh << endl;return 0;
}
即写成友元的形式。这样既不会传入默认的this指针,又可以访问类内的成员。
结语
只是兴起分析了一下cout,本人水平有限或许有纰漏,还望指正。
相关文章:
cout源码浅析
目录 cout源码浅析 那么对于没有定义在这之中的要怎么办呢? 实际使用 结语 首先来看我从cplusplus中截取的这张图: 注意最下面这一行字。cout其实是ostream的一个标准对象object。而上面则演示了一些继承关系。 好的,理解了之后…...
发送Ajax get请求详解
发送AJAX get请求,前端代码: <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>ajax get请求</title> </head> <body> <script type"text/java…...
SQL语句
创建及删除数据库和表 CREATE DATABASE 数据库名; CREATE DATABASE school; 创建新的表 CREATE TABLE 表名(字段1 数据类型,字段2 数据类型[,...] [,PRIMARY KEY (主键名)]); #主键一般选择能代表唯一性的字段,不允许取空值(NULL),值也不允许重复&…...
Mysql 学习(八)单表查询方法二
复杂查询 上一节说了5种访问类型的查询,这一节就来说说关于这些比较复杂的查询 情况一:多个二级索引查询 sql:SELECT * FROM index_value_table WHERE value1 abc AND value2 > 1000;搜索条件: value1 等于 abcvalue2 大于…...
安卓系统下的截屏和录屏
可以抓取手机屏幕画面(屏幕截图),也可以录制屏幕画面视频。拍摄屏幕后,可以查看、编辑和分享所拍的图片或视频。 抓取屏幕截图 打开要抓取的屏幕。视手机情况执行下列一个操作,3种方法看你手机有效的: 同…...
行为型模式-中介者模式
中介者模式 概述 一般来说,同事类之间的关系是比较复杂的,多个同事类之间互相关联时,他们之间的关系会呈现为复杂的网状结构,这是一种过度耦合的架构,即不利于类的复用,也不稳定。例如在下左图中…...
辅助驾驶功能开发-功能规范篇(16)-2-领航辅助系统NAP-功能ODD定义
1.系统定义 智能驾驶系统包含行车场景功能和泊车场景功能,行车场景功能包括安全ADAS功能、基础ADAS功能和高阶ADAS功能三大类,本文档定义高阶ADAS功能中的导航辅助驾驶功能用例。 1.1.高阶ADAS功能列表 功能需求ID 功能分类 功能名称...
PMP/高项 06-项目成本管理
项目成本管理 概念 项目成本管理 项目成本管理又被称为项目造价管理,是有关项目成本和项目价值两个方面的管理,是为保障以最小的成本实现最大的项目价值而开展的项目专项管理工作。 确保在批准的项目预算内完成项目 成本管理内容 规划成本管理 制定项目…...
XXL-JOB中间件【实现分布式任务调度】
目录 1:XXL-JOB介绍 2:搭建XXL-JOB 2.1:调度中心 2.2:执行器 2.3:执行任务 3:分片广播 1:XXL-JOB介绍 XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学…...
Vue3+Element Plus环境搭建和一键切换明暗主题的配置
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。而Element Plus是一款基于Vue3面向设计师和开发者的组件库。 最终效果: 环境搭建 已安装 16.0 或更高版本的 Node.js,终端: npm init vuelatest这一…...
Leetcode326. 3 的幂
Every day a leetcode 题目来源:326. 3 的幂 相似题目:342. 4的幂 解法1:递归 代码: /** lc appleetcode.cn id326 langcpp** [326] 3 的幂*/// lc codestart class Solution { public:bool isPowerOfThree(int n){if (n <…...
【运动规划算法项目实战】如何在栅格地图中实现Dijkstra算法
文章目录 简介一、算法介绍1.1 Dijkstra算法流程1.2 Dijkstra算法伪代码二、代码实现2.1 ROS实现2.2 RVIZ演示三、总结简介 Dijkstra算法是一种用于图中单源最短路径的贪心算法。在计算机科学和网络设计中广泛应用。该算法从起点开始,通过优先选择距离起点最近的未标记节点来…...
【算法】一文彻底搞懂ZAB算法
文章目录 什么是ZAB 算法?深入ZAB算法1. 消息广播两阶段提交ZAB消息广播过程 2. 崩溃恢复选举参数选举流程 ZAB算法需要解决的两大问题1. 已经被处理的消息不能丢2. 被丢弃的消息不能再次出现 最近需要设计一个分布式系统,需要一个中间件来存储共享的信息…...
【软考高级】2022年系统分析师综合知识
1.( )是从系统的应用领域而不是从系统用户的特定需要中得出的,它们可以是新的功能性需求,或者是对已有功能性需求的约束,或者是陈述特定的计算必须遵守的要求。 A.功能性需求 B. 用户需求 C.产品需求 D.领域需求 2.对于安全关键系…...
关于AI未来的思考和应用场景
关于AI未来的思考和应用场景 AI(人工智能)是当今最热门的技术领域之一,它已经在多个领域产生了深远的影响,如医疗、金融、制造业等。未来,AI将继续发展,并在更多领域产生重要的影响。 AI的未来发展方向有…...
智慧城市规划数字化管理:数字孪生技术的创新应用
随着智能城市的不断发展,数字孪生技术也开始在智慧城市的建设中得到了广泛应用。数字孪生作为一种数字化的复制技术,它可以模拟真实世界中的实体和过程。 在城市规划方面,数字孪生可以帮助城市规划师更加直观地了解城市的整体规划和发展趋势&…...
开心档之C++ 指针
C 指针 学习 C 的指针既简单又有趣。通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。 正如您所知…...
零基础搭建私人影音媒体平台【远程访问Jellyfin播放器】
文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 转载自内网穿透工具的文章:零基础搭建私人影音媒体平台【远程访问Jelly…...
Abstract Expressionist
古董地图画集 10大最有名的抽象艺术家 抽象表现主义是现代许多不同艺术思想和表达流派中最奇特的艺术运动之一。这场运动开始从社会变革中涌现出来,恰逢第二次世界大战的最后几周和几个月。 这一次,来自世界各地的人们开始欢迎在经历了多年有史以来最致…...
【郭东白架构课 模块二:创造价值】24|节点四:如何减少语义上的分歧?
你好,我是郭东白。上节课我们通过一个篇幅比较长的电商案例,详细展示了为什么在架构活动中会出现语义分歧。同时也描述了,架构师在统一语义这个环节中所要创造的真正价值是什么。即,看到不同角色之间语境的差异,然后通…...
ANSYS Workbench网格划分进阶:扫掠、多区与2D网格的实战精解
1. 扫掠网格划分:从原理到实战技巧 第一次用ANSYS Workbench做薄壁结构分析时,我对着那个复杂的几何模型发呆了半小时——到底该选哪种网格划分方法?直到掌握了扫掠网格的精髓,才发现原来处理这类问题可以如此高效。扫掠网格特别适…...
CANN/asc-devkit ReduceProd API文档
ReduceProd 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。 项目地址: https://gitcode.com…...
SAP顾问实战笔记:手把手配置OBYC,搞定采购收货到发票校验的自动记账
SAP财务自动化实战:从采购收货到发票校验的OBYC全链路配置指南 当财务部门每月需要处理上千笔采购业务时,手工记账不仅效率低下,还容易出错。SAP系统中的OBYC配置正是解决这一痛点的关键——它能实现从采购收货到发票校验的全自动会计凭证生成…...
DRAM控制器优化与内存带宽保障技术解析
1. DRAM控制器架构演进与优化实践现代计算机系统中,DRAM控制器的设计直接影响着内存子系统的整体性能表现。传统控制器采用统一事务队列架构,这种设计虽然实现简单,但在实际应用中暴露出明显的性能瓶颈。让我们深入分析这种架构的局限性及其优…...
Transformer注意力机制数据流优化与MMEE方法实践
1. 注意力机制数据流优化概述在Transformer架构和大型语言模型(LLM)中,注意力机制的计算开销通常占整体工作负载的60%以上。随着模型处理序列长度的不断增加,注意力计算面临的性能瓶颈日益凸显——其计算复杂度与序列长度呈二次方关系。这种特性使得传统…...
TeamHero:基于规则引擎的智能任务自动化分配系统设计与实战
1. 项目概述与核心价值 最近在GitHub上看到一个挺有意思的项目,叫“TeamHero”,作者是sagiyaacoby。乍一看这个名字,你可能会联想到团队协作或者英雄联盟,但实际上,它是一个专注于自动化团队管理与任务分发的工具。简…...
5分钟解放双手:淘宝淘金币自动化脚本终极指南
5分钟解放双手:淘宝淘金币自动化脚本终极指南 【免费下载链接】taojinbi 淘宝淘金币自动执行脚本,包含蚂蚁森林收取能量,芭芭农场全任务,解放你的双手 项目地址: https://gitcode.com/gh_mirrors/ta/taojinbi 淘金币自动化…...
英伟达巨额投资,四大云巨头财报亮眼,半导体产业扩张背后隐忧浮现
物理世界产能成为瓶颈云收入快速增长支撑巨头大规模投资。2026年第一季度,谷歌云、微软Azure、亚马逊AWS云业务表现出色,四家公司云业务合计季度营收超700亿美元,同比增长超40%。但物理世界产能受限,谷歌、微软、亚马逊订单积压严…...
创优必看!鲁班奖工程的八项基本要求
创优必看!鲁班奖工程的八项基本要求 作为建筑工程行业的最高级别奖项,鲁班奖的评选工作严格贯彻执行国家有关基本建设的法律、法规和方针政策,以及国家、行业现行的技术标准、施工规范和技术规程。那么,什么样的工程才能荣获鲁班奖呢? 本文根据《鲁班奖评选工作细则》总…...
2025届毕业生推荐的六大降AI率神器解析与推荐
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 对于有那种需求去规避人工智能内容检测的用户来讲,在这儿推荐使用专业的降AI率网…...
