做題的時候,我經常用while(cin>>a)這樣的模式來 實現 連續輸入 或 統計輸入連續數字的個數 等功能,但是一直都對其中的原理感到迷迷糊糊,我覺得只會用怎麼行,還得搞明白 其所以然 來。所以看了一些資料和大佬們的博客,然後寫這樣一篇博客來解釋其中的原理。
使用情形 以及 跳出的while(cin>>a)的方法
while(cin>>a)的意思是 只要輸入的值有效,那麼就執行while體內的語句。while循環結束 (跳出流對象)的時候 ,通過檢測其流的狀態來判斷結束:
(1)若流是有效的,即流未遇到錯誤,那麼檢測成功。
(2)若遇到一個無效的輸入時(例如:上述的a是int類型的,但輸入的值不是一個整數),istream對象的狀態會變爲無效,條件就爲假。
(3)若遇到文件結束符也可以跳出while(cin>>a)。在windows系統中,輸入文件結束符的方法是先按Ctrl+Z,然後再按Enter。在UNIX系統中,包括Mac OS X系統中,文件結束輸入爲Ctrl+D。
特別的:在while循環中以EOF作爲文件結束標誌(end of file),EOF是針對文件輸入的情況,文件指針如果指向文件尾就會等於EOF。 這種以EOF作爲文件結束標誌的文件,必須是文本文件!在文本文件中,數據都是以字符的ASCII代碼值的形式存放。我們知道,ASCII代碼值的範圍是0~127,不可能出現-1,因此可以用EOF作爲文件結束標誌。
原理剖析
跳不跳出while(cin>>a)循環 關鍵在於 cin>>a 的值 是不是 假(0)。
運算符返回的是流對象的引用,cin是一個流對象,而>>運算符返回左邊的流對象,也就是說cin>>val返回cin,於是while(cin>>val)就變成了while(cin),問題就變成了一個流對象在判斷語句中的合法性。 (注意一下:右移運算符>>的結合方向是從左到右 輸入流類(istream) 把 >> 重載了)
如果定義了一個類(沒有重載 void * 和 ! 運算符的類),然後定義該類的對象,然後使用 while 或者 if 語句來判斷它是不合法的。可是爲什麼while(cin) 和 if(cin)都是合法的呢?原來 輸入流類(istream) 重載了 void* 和 ! 這兩個運算符!!
打開iostream.h文件,可以看到 operator void *() const和bool operator!() const 這兩個函數。這兩個函數使得流對象可作爲判斷語句的內容。
可是重載這兩個運算符 跟可作爲判斷語句的內容有什麼關係?
原因是這樣的:
(以下用if來解釋,其實while和if 在處理這方面的情況是一樣的)
①先來測試下沒有重載上述運算符的情況
#include <iostream>
using namespace std;
class A
{
};
int main()
{
A a;
if(a)
cout<<"YES";
return 0;
}
不出所料,報錯信息如下:
[Error] could not convert 'a' from 'A' to 'bool'
分析:說明編譯器編譯的時候 不能 將對象a轉換爲bool類型,不能轉換的話 也就無法判斷if裏判斷語句的內容是 真(非0) 是 假(0) 了。
①改進:重載了void*後
#include <iostream>
using namespace std;
class A
{
public:
//返回值類型 就是void* 不用額外寫
operator void*()
{
return (void*)0;
}
};
int main()
{
A a;
if(a)
cout<<"YES";
return 0;
}
分析:這樣寫後,編譯就沒問題了。
因爲在a不能直接轉換爲bool類型的情況下,會試圖先進行 (void*)a 這樣的操作,這個操作會直接調用A類的成員函數operator void*(),這個函數返回一個void*的數據,所以這裏的if(a)其實就是if((void*)a),然後根據強制轉換後的結果來進行if的判斷,這裏顯然應該不輸出。如果改成return (void*)this,那麼就可以看到運行後輸出了YES
②然後來看看下面的代碼
#include <iostream>
using namespace std;
class A
{
public:
//返回值類型 就是void* 不用額外寫
operator void*()
{
cout<<"a";
return (void*)0;
}
};
int main()
{
A a;
if(!a)
cout<<"YES";
return 0;
}
輸出:aYes
分析:當程序執行到if(!a)時,對於!a,同樣是由於a無法直接轉化爲bool類型,所以!a就等同於!((void*)a),由於(void*)a的值爲0 (調用了A類的成員函數operator void*(),然後返回了(void*)0 ),所以!a爲真,那麼就可以執行if(!a)裏面的語句了。
②改進:重載了 ! 運算符
#include <iostream>
using namespace std;
class A
{
public:
//返回值類型 就是void* 不用額外寫
operator void*()
{
cout<<"a";
return (void*)0;
}
bool operator!()
{
cout<<"A";
return true;
}
};
int main()
{
A a;
if(!a)
cout<<"YES";
return 0;
}
輸出:AYes
分析:重載了運算符!後, !a就直接執行了operator!()函數,該函數返回true,所以可以執行if(!a)裏面的語句。