C++之“流”-第2课-C++和C标准输入输出同步

为什么C++和C的标准输入输出不同步时,数据会混乱?同步会带来多大性能损失?为什么说这个损失通常不用太在乎?

0. 课堂视频

C++之“流”-第2课:和C输入输出的同步

1. 理解cin和cout的类型与创建过程

std::cout 是std::ostream类型的一个变量;而 std::ostream是std::basic_ostream<char>模板类的类型别名( typedef )。std::cin是std::isteram类型的一个变量,而std::istream是std::back_istream<cahr>模板类的类型别名。两个模板类中的 “char” 参数,表明二者都是基于普通 字符(char)作为最小输出或输入单位。如果改为 wchar_t,则以UNICODE字符串作为基本输入输出单位。

正如上一节课所说,std::ostream和std::istream都是抽象概念的流,无法直接创建对应的输出或输入流对象。

注意,C++中的“抽象”概念,和 Java 这样更加纯粹的“面向对象”的编程语言有所不同。Java 中的“抽象”,通常使用:“什么实事都不做,只负责定要求” 的接口(interface)表达。C++中有更多不同的方式来表达抽象概念,可以同样“什么事都不做,只负责定要求”的纯虚类,也可以是“做了很多基础的事,但禁用了特定构建方法”的方式。两种方式的共同表现是:不让用户直接创建对象。

std::ostream和std::istream对外开放的构造方法,都要求一个“流缓存区/stream_buf”入参。以输出为例,我们可以:

  • 设计并实现一个内存输出缓存区,传入后以得到一个内存输出流的基本功能;
  • 设计并实现一个文件输出缓存区,传入后以得到一个文件输出流的基本功能;
  • 设计并实现一个网络输出缓存区,传入后以得到一个网络输出流的基本功能;

那么,为std::istream的构造函数传入一个键盘输入流缓存区,就能得到一个标准输入流,即std::cin;而为std::ostream传入一个屏幕输出流缓存区,就能得到一个标准输出流,即std::cout。但实际上,C++程序中的std::cin和std::cout对象,都是C++库自动创建出来的,并且不允许用户手工创建二者。为什么呢?因为对一个程序来说,标准输入设施应该只有一个,标准输出设施也应该只有一个;如果用户自己创建,就挡不住有用户创建出一打标准输入流或标准输出流了。

在Windows的控制台(console)或Linux下的终端(terminal)里,键盘被称为程序的标准输入设备,屏幕被称为程序的标准输出。并且,无论一台电脑接多少个键盘(少见),在逻辑上都会被当作一个键盘;同理,无论一台电脑接多少个屏幕(常见),在逻辑上也都会被当作一个屏幕。因此,cin 和 cout 本质上是一种“单例”,即整个程序中,只能一个标准输入流,一个标准输出流。

这种“一个程序里,某种类型的对象只有一个”的逻辑的实现,有专门的,称为“单例模式”的设计模式来实现。C++实现 cin 和 cout 的单例保障倒很简单:使用默认构造函数(没有任何入参)来创建特定对象,再把该默认构造函数的访问权限设置为私有(private)或保护的(protected),在gc++的实现中使用的是后者。标准库内部可以通过 “友元”加“派生”的方式,实现对基类受保护的默认构造函数的调用。

一旦调用std::ostream的默认构造函数,由于没有入参,也就没有外部传入的输出缓存区,此时标准库将自动创建标准输出流的缓存区,从而创建出标准输出流,即:std::cout对象。标准输入流的创建过程与此类型,同样是调用默认构造函数,然后自行创建、关联和键盘输入缓存区,从而创建出 std::cin。

以上调用过程都是在程序主函数 main() 开始之前,就执行完毕,因此我们的程序在一开始就能够方便地使用std::cin和std::cout。事实上,在 main() 之前我们就可以使用了。如果在 main() 函数之前就开始执特定代码,这是C++的另一个知识点,不在此讲解。

2. 数据输入输出次序冲突问题的出现

到现在,一切看起来很完美:cin和cout是自动创建的,并且各自只会有一份,不会冲突……但是,考虑到C++的一个重要的历史使命:兼容C语言,问题就来了——

C 语言有自己的输入输出机制,并且本质上,底层也需要用到输入或输出缓存区。上一节课我们说过,这个缓存区本质是一个数据队列,一个“有次序保障”的数据队列。C++尽管做到了一个程序只有一个C++输入流或一个C++输出流,但加上C的队列,现在,一个C++程序会有两个输入队列、两个输出队列。

C/C++两套输入输出队列

这就有点像现实生活中的某种排队现象:入口或出口只有一个,但人们排了两条队,两条队伍各自的内部数据都有次序保障,但是,当门就在眼前,两条队伍如何通过一个门呢?无论是互相礼让,还是争先抢后,都无法保障复原原始的数据次序。

3. 混合输入,同步对比不同步

代码:

#include <cstdio> // C 语言的标准输入输出库
#include <iostream>using namespace std;int main()
{ios_base::sync_with_stdio(false); // 不同步!!!int i, j;scanf("%d", &i); //用C的方式输入  icin >> j; // 用C++的方式输入 jcout << i << ", " << j << endl;return 0;
}

4. 混合输出,同步对比不同步

代码:

#include <cstdio>
#include <iostream>using namespace std;int main()
{ios_base::sync_with_stdio(false);for (int i=0; i<3; ++i){printf("hello from printf!\n");cout << "hello from cout.\n";}return 0;
}

5. 同步与不同步性能对比

代码:

#include <ctime>
#include <cstdio>
#include <iostream>using namespace std;int main()
{ios::sync_with_stdio(false);clock_t beg = clock();for (int i=0; i<30000; ++i){cout << "hello world.";}clock_t end = clock();cout << "\n" << (end - beg) * 1000 / CLOCKS_PER_SEC << "ms." <<endl;return 0;
}

注意,程序使用 sync_with_stdio(false) 取消 C++和C的标准输入输出同步,该操作是不可逆的,即后续无法通过 sync_with_stdio(true) 恢复 同步。

6. 为什么不用太在乎C++标准输入输出的性能?

C++常用以写以下程序:

类型典型应用描述大致占比输入输出性能
后台服务或底层组件网络服务、防火墙不直接面向用户,不使用标准输入输出25%不在乎
GUI程序Photoshop、Office、游戏使用系统GUI作为输入输出20%不在乎
基础工具命令行文件处理工具:压缩、图片处理虽然在命令行运行,但几乎没有输入输出15%不在乎
简单命令行工具各类命令行客户端程序:libcurl,文件列表低频使用标准输入输出20%不在乎
非性能敏感的控制台应用用户开发的简单命令行应用,比如处理excel表格性能不敏感15%不在乎
性能敏感的控制台应用信息学竞赛程序、远程日志监控等性能敏感,大量标准输入输出操作会影响程序性能5%在乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/17374.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu系统Discover软件中心简介

Discover软件中心是Ubuntu操作系统中默认的软件管理工具&#xff0c;它提供了一个图形用户界面(GUI)来帮助用户浏览、搜索、安装和卸载软件包。Discover软件中心是Ubuntu软件中心(Ubuntu Software Center)的继承者&#xff0c;它在Ubuntu 16.04 LTS版本中首次被引入&#xff0c…

添加、修改和删除字典元素

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 由于字典是可变序列&#xff0c;所以可以随时在字典中添加“键-值对”。向字典中添加元素的语法格式如下&#xff1a; dictionary[key] value 参数…

You don‘t have enough free space或者no space left on device异常

1.磁盘空间不足 Linux安装软件显示 You dont have enough free space 或者docker拉镜像时&#xff0c;出现磁盘空间不足的情况 no space left on device 如果你是ubuntu系统。查看磁盘空间 df -h 多半是这个目录满了/dev/mapper/ubuntu--vg-ubuntu--lv 大多情况我们只希望扩…

学习编程对英语要求高吗?

学习编程并不一定需要高深的英语水平。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在后台发给你。 虽然一些编程资源和文档可能…

typora自动生成标题序号(修改V1.0)

目录 带序号效果图 解决方法 带序号效果图 解决方法 1.进入文件夹&#xff1a;文件–>偏好设置–>外观–>主题–>打开主题文件夹 2.如果没有base.user.css文件&#xff0c;新建一个。如果有直接用记事本打开&#xff0c;把下面代码拷贝进去保存。 /** initiali…

【JUC编程】-多线程和CompletableFuture的使用

多线程编程 文章目录 多线程编程[toc]引言创建多线程的方式继承Thread类实现Runnable接口实现Callable接口Callable和Runnable的区别 Lambda表达式 线程的实现原理Future&FutureTask具体使用submit方法Future到FutureTask类Future注意事项局限性 CompletionService引言使用…

第八大奇迹

目录 题目描述 输入描述 输出描述 输入输出样例 示例 输入 输出 运行限制 原题链接 代码思路 题目描述 在一条 R 河流域&#xff0c;繁衍着一个古老的名族 Z。他们世代沿河而居&#xff0c;也在河边发展出了璀璨的文明。 Z 族在 R 河沿岸修建了很多建筑&#xff0c…

java如何向数组中插入元素

java的数组是不可改变的&#xff0c;因此如果要向数组中插入新的元素&#xff0c;需要新建一个数组&#xff0c;新的数组元素个数减去老数组元素个数的差大于等于要插入新的元素数量。 假如说要插入一个数组元素&#xff0c;需要把新元素插入到中间&#xff0c;把新的数组分为…

Vue组件通讯⽗组件中通过 provide 来提供变量,然后在⼦组件中通过 inject 来注⼊变量例子

在Vue中&#xff0c;provide 和 inject 主要用于依赖注入&#xff0c;允许祖先组件向其所有子孙组件提供一个依赖&#xff0c;而不论组件层次有多深。这在开发高阶插件/组件库时特别有用。 以下是一个简单的例子&#xff0c;演示了如何在父组件中使用 provide 提供变量&#x…

软件测试面试题(八)

一&#xff1a;TestDirector有哪些功能&#xff0c;如何对软件测试过程进行管理&#xff1f; 需求管理 定义测试范围 定义需求树 描述需求树的功能点 测试计划 定义测试目标和测试策略 分解应用程序&#xff0c;建立测试计划树 确定每个功能点的测试方法 将每个功能点连接…

Ps 滤镜:消失点

Ps菜单&#xff1a;滤镜/消失点 Filter/Vanishing Point 快捷键&#xff1a;Ctrl Alt V 两条平行的铁轨或两排树木连线相交于很远很远的某一点&#xff0c;这点在透视图中叫做“消失点”&#xff0c;也称为“灭点”。 消失点 Vanishing Point滤镜主要用于在图像中处理具有透视…

C++入门3——类与对象(2)

1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。可是空类中真的什么都没有吗&#xff1f; 其实并不是的&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xf…

libmodbus开发库介绍

目录 功能概要源码获取源码内容结构源码与移植 功能概要 libmodbus是一个免费的跨平台支持RTU和TCP的Modbus库&#xff0c;遵循LGPL V2.1协议。libmodbus支持Linux、Mac Os X、FreeBSD、QNX和Windows等操作系统。libmodbus可以向符合Modbus协议的设备发送和接收数据&#xff0…

vector的reverse和resize区别

一 代码 #include "stdafx.h" #include <iostream> #include <vector> using namespace std;class TEST{ public:TEST(){std::cout << "construct t" << std::endl;} };int main() {std::cout << "hello,world" …

《Python侦探手册:用正则表达式破译文本密码》

在这个信息爆炸的时代&#xff0c;每个人都需要一本侦探手册。阿佑今天将带你深入Python的正则表达式世界&#xff0c;教你如何像侦探一样&#xff0c;用代码破解文本中的每一个谜题。从基础的字符匹配到复杂的数据清洗&#xff0c;每一个技巧都足以让你在文本处理的领域中成为…

【一站式学会Kotlin】第十三节:kotlin语言中的解构

作者介绍: 百度资深Android工程师T6,在百度任职7年半。 目前:成立赵小灰代码工作室,欢迎大家找我交流Android、微信小程序、鸿蒙项目。= 一:通俗易懂的人工智能教程:https://www.captainbed.cn/nefu/ 点一下,打开新世界的大门。 二:【一站式学会Kotlin】免费领取:作者…

SQLSyntaxErrorException: FUNCTION dbname.to_timestamp does not exist

由于MySQL数据库高版本&#xff08;如8.x&#xff09;中有to_timestamp(&#xff09;函数&#xff0c;低版本中&#xff08;如5.7.x&#xff09;没有这个函数&#xff0c;服务运行报错。 自己创建函数实现功能&#xff0c;创建语句如下&#xff1b; DELIMITER // CREATE FUN…

如何使用ChatGPT撰写短视频爆款文案

在这个快速发展的数字时代&#xff0c;短视频已经成为最受欢迎的娱乐和信息获取方式之一。对于内容创作者来说&#xff0c;如何制作出爆款短视频&#xff0c;吸引更多观众的注意力&#xff0c;是他们面临的一大挑战。文案&#xff0c;作为视频内容的灵魂&#xff0c;起着至关重…

ESP32 - Micropython ESP-IDF 双线教程 中断和定时器 (1)

ESP32 - Micropython ESP-IDF 双线教程 中断和定时器 ESP32中断ESP32定时器归纳ESP32 - Micropython 定时器示例代码代码介绍 ESP32 - IDF 定时器示例代码代码解释ESP32-IDF定时器使用介绍 ESP32中的中断和定时器是两种重要的硬件特性&#xff0c;它们在嵌入式系统开发中扮演着…

系统思考—战略沙盘推演咨询服务

今日与JSTO团队一起学习了《战略沙盘推演咨询服务》。通过沙盘体验&#xff0c;我深刻感受到组织与战略就像一张皮的正反两面。在转型过程中&#xff0c;即使战略非常明确&#xff0c;团队成员由于恐惧和顾虑&#xff0c;往往不愿意挑战新的业务&#xff0c;从而难以实现战略目…