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.pswp.cn/bicheng/17374.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/17374.shtml
英文地址,請注明出處:http://en.pswp.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;從而難以實現戰略目…