標籤:

iostream 是 C++ 的缺陷嗎,為什麼?

想要了解iostream和scanf/printf的坑點。


C++標準庫的stream性能比C/C++的sprintf() 差。以下是我對浮點數轉換至字元串的測試:

VC2013 x64

http://rawgit.com/miloyip/dtoa-benchmark/master/result/corei7920@2.67_win64_vc2013.html

GCC 4.8 x64

http://rawgit.com/miloyip/dtoa-benchmark/master/result/corei7920@2.67_cygwin64_gcc4.8.html

GCC的結果中雖然兩者差距比較少,但比VC慢一個數量級……

測試源碼在 miloyip/dtoa-benchmark · GitHub

這個並不是完整的測試,因為還沒考慮其他數據類型。但stream的設計本身就有很多無法避免的overhead,詳細情況可參考 @陳碩的 C++ 工程實踐(7):iostream 的用途與局限。

敝開性能方面,stream的主要好處是開放擴展。


各種語言,不論是從一開始是同意還是抵制,最後都提供了 fmt string 形式的 I/O。本身說明 fmt string 是一種經過時間檢驗的可靠方案。所謂的 type unsafe 等等 fmt string 的缺陷,其實都是紙上談兵的問題,實際工程里根本沒那麼多問題。為了原生而原生就沒什麼意義了。

看看 C++ FQA Lite: Input/output via amp;amp;amp; and amp;amp;amp;

printf("0x%08xn", x);

對比

std::cout &<&< std::hex &<&< std::setfill(0) &<&< std::setw(8) &<&< x &<&< std::dec &<&< std::endl;

更何況,在實際工程中,format string 本身就是一種不適合硬編碼的資源。把 format 嵌到語言的類型系統里是非常不實際的。就像你要把系統的 JSON 配置嵌入到類型檢查中。工程中本來就需要用局部的弱類型來換取靈活性。


不是。舉幾個簡單例子來闡述理由。

#include &
using namespace std;

class A
{
public:
explicit A(int i) : data(i){}

private:
int data;

friend ostream operator &<&< (ostream out, const A a); }; ostream operator &<&< (ostream out, const A a) { out &<&< a.data; return out; } int main() { A a(47); cout &<&< a &<&< endl; return 0; }

如果不使用iostream,那麼你使用printf如何自定義輸出a?所以,這裡體現出了iostream的擴展性。

#include &
#include &
using namespace std;

void f(istream in)
{
// balabala
}

int main()
{
ifstream in("a.txt");
f(in);
f(cin);
return 0;
}

由於iostream是實際的class,其具有繼承性,所以你可以如此輕易的實現多態,而stdio卻是FILE*。

char c;
scanf(" %c",c); // There is one additional space
cin &>&> c;

這個高下立見了,無論是簡易性還是類型安全,以及坑都比scanf這樣使用%指定來的好,並且讀取一個字元的時候還需要多使用一個空格,這無疑是容易跳坑的地方。而使用%在自定義輸出小數點等方式會更方便一些,而使用cin/cout的控制小數位的話,代碼不大好看,這是不足的地方。

還有一些未詳細闡明的如scanf/printf更容易出現bufferoverflow這樣的問題(所以有了_s版本),總體來說iostream不是缺陷。如果真說缺陷,也許是在切ACM題目的時候容易超時吧。


討論之前多問一句——題主的問題背景是 OI/ACM 競賽么?(感覺做演算法競賽的不少人都產生過這個疑問。)

如果是的話,那麼現有的幾個優秀答案其實對題主幾乎沒有什麼幫助。 iostream 使用在 stdio 上時的確明顯慢一截,除了較為複雜的類型系統和機制設計造成的 overhead 以外,還有一大塊性能損失是和 C 風格 IO 同步導致的(針對 stdio 而言),關閉之後會好很多(切記一旦關了就不要混用兩種 IO 方式了):

std::ios_base::sync_with_stdio(false);

不過我始終覺得,把題目出到需要卡 IO 速度是不對的(題目設計水平不足的表現)……

實際使用中的其他問題,我也很推薦 @Milo Yip 答案中提到的陳碩的那篇文章。


我覺得雖然效率上的確是有區別,但以stream的方式來代表文字流實際上是非常合適的,可擴展性也高,也沒有老C IO那麼麻煩。

再說了C++了不搞個OO的輸入可以嗎(滑稽

順便說一下速度問題,main一開始寫一個

std::ios::sync_with_stdio(false);

cin.tie(NULL);

可以有一定幅度的速度提升(但是千萬不能和printf/scanf混用)


一個沒什麼人用的東西,管他幹啥?

C++ 工程實踐(7):iostream 的用途與局限


目前C++語法下面,最好的print應該是長這個樣子的

class ITextWriter abstract
{
protected:
virtual void WriteText(wchar_t* text) = 0;

public:
template&
ITextWriter Write(const wstring pattern, TArgs ...args)
{
...
}

template&
ITextWriter WriteLine(const wstring pattern, TArgs ...args);
{
Write(pattern, std::forward&(args)...);
WriteText(L"
");
return *this;
}
};

用法:

Console.Write(L"I am {0}, bitch", L"the CEO");

File(L"C:Fuckers.txt")
.WriteLine(L"Ali")
.WriteLine(L"Baidu")
.WriteLine(L"G{0}{0}gl{1}", 0, L"e");


iostream比stdio慢很正常,它能做的事兒更多。

另外,實在想像不出把屏幕列印的效率做到極致有什麼意義,任何時候往一個外圍io設備輸入輸出都意味著這段代碼執行的瞬間是對效率不敏感的,如果敏感就不應該直接輸出到外圍設備了。


就效率來說...我覺得是...不過這個是因為iostream的目標是將一切io都做成流,內部class繼承結構複雜的嚇人(一個是歷史遺留,一個是I/O stream問題本來就複雜)...而且為了兼容C的IO流,所以要打開流同步開關- -所以速度就更慢了,關閉流同步開關可以達到scanf和printf的效率,但是....如果你用的庫裡面用了C的IO流,就可能會有問題...又不能關- -....這個比較蛋疼....

就可擴展性和類型安全性來說,比C的scanf和printf高不知道哪裡去了~也沒有醜陋的%X的形式,也可以輕鬆擴展到自定義類型,又可以有類型系統的約束.多好啊~上手也簡單,坑也少.

就控制輸出格式來說...確實iostream這套代碼不太好看.....不過還是可以用噠~

so....iostream和C的scanf printf不是根本不應該放一起去比較- -scanf和printf適合在注重IO速度的場合(刷ACM).或者輸出有複雜的格式控制問題.

iostream適合用在簡單的輸出格式.io效率不是性能瓶頸,簡單易用.附帶類型安全~如果你能保證你的代碼和依賴庫裡面沒有用到C庫io流,還可以關閉流同步~體驗魚和熊掌都可兼得的感受哦~=.=多好呀~


我覺得彙編的效率高多了,我為什麼不用


iostream效率上是足夠的,而且方便調試(幾乎所有程序都是需要log的)。

你刷OJ刷演算法題,除了線段樹之類需要大量io的題,iostream沒什麼關係,如果他連這個都卡就真沒什麼必要了。

要知道printf畢竟屬於C library部分的東西,並不夠「原生」,而且和C++當初的設計初衷並不吻合。像是ostream可以進行自定義直接輸出對象等等,std::printf就不能夠。


朋友為什麼不試試這個庫呢 Overview - fmt 4.0.0 documentation


關鍵在於,你可以用c++同時實現這兩種輸出,所以不能說iostream就是一個缺陷。

在對io速度要求不高的時候,完全可以用iostream,畢竟代碼簡單直觀(一些特殊情況除外),不用弄一堆通配符;追求io速度的時候就用stdio就好。


我覺得還行吧,以前我試過在OJ平台上輸出100萬行「hello world」,cout佔用的時間不多,endl倒是多用了太多時間……不過管他呢,哪有那麼多需要大量輸出的場合啊


不是缺陷,是病態設計毫無意義的垃圾,話說c++標準委員會那幫人就喜歡玩這些,還有現在的std::thread這些玩意,都是玩具車級別的,偏偏有人信了然後開它上路還想飈車...


推薦閱讀:

如何評價 Herb Sutter 的 C++ 提案:metaclasses?
C 語言局部變數,堆與棧的問題?
現代OpenGL怎麼繪製四邊形網格結構?
微軟究竟遇到了什麼問題使得他們到現在都無法在 C1 中實現兩步名稱查找?
編程除了學語言還要學什麼?

TAG:C |