2012-09-01 31 views
1
std::istream & operator >>(std::istream & ins, Rational & target) 
{ 
    int num, den; 
    char symb;   
    std::cout << "Please enter a rational number: "; 
    ins >> num >> symb >> den; 
    std::cout << std::endl; 

    if(validateInput(num, symb, den)){ 
     target = Rational(num, den);    
     return ins; 
    } 
    else{ 
     std::cin >> target; 
    } 
} 

bool validateInput(int num, char symb, int den) 
{ 
    if(symb != '/'){ 
     std::cout << "Error: Illegal format. Please use '2/4'." << std::endl; 
     return false; 
    } 
    if((static_cast<int>(num) != num) && (static_cast<int>(den) != den)){ 
     std::cout << "Error: Not a valid rational number." << std::endl; 
     return false; 
    } 
    if(den == 0){ 
     std::cout << "Error: Cannot divide by 0." << std::endl; 
     return false; 
    } 

    return true; 
} 

它採用'x/y'格式的有理數,例如2/4。 它工作正常,如果我鍵入它的權利。如果我輸入2p4,它會給出正確的錯誤(我錯過了一個'/'),然後要求一個新的號碼。如果分母中有0,它也會報告錯誤並要求輸入一個新的數字。當我檢查錯誤時,在我的過載的>>運算符中出現無限循環

但檢查看看它是否是一個有效的數字似乎沒有工作。如果我輸入'a/4',它會無限循環,直到它崩潰。我無法弄清楚爲什麼。檢查調試器,它會返回到ins >>語句,但不會詢問用戶的任何內容。

我假設我的邏輯是錯誤的地方。請注意,我對C++相當陌生,仍在學習。我之前嘗試異常處理,仍然沒有正確地學習,所以我重新回到了我更熟悉的東西上。

謝謝!

+0

這段代碼似乎違背了操作符重載的良好做法,可以改進。首先,不要通過cout提示用戶輸入值,因爲控制檯可能不可用,比如應用程序在後臺運行。其次,'validateinput()'中的static_cast語句將始終工作,因爲輸入已經被分解爲這些類型的行,這些行類型爲ins >> num >> symb >> den;'。第三,驗證應該在Rational構造函數中執行。它應該拋出一個異常,如果這些值是無效的,但我接受你正在努力。 – akton

+0

我可能應該在我的文章中更具體。這是一個控制檯應用程序,這是我分配給課程的一個項目。我希望Rational構造函數能夠進行驗證,但問題是,該項目特別要求程序要求用戶以這種格式輸入一個有理數:'2/4'。所以當它要求我輸入第一個數字時,我必須輸入2/4。理性構造函數將無法檢查/是否被輸入,所以這就是爲什麼我在>>函數中進行驗證的原因。我可能在這方面非常錯誤,因爲我很新,所以如果我是的話請糾正!謝謝 –

+0

不夠公平。在這種情況下,首先,我不會在操作員超載時提示用戶。出於上述原因,我會將其移至不同的功能。其次,使用'ins >> num >> symb >> den;'拉入數據可以在每個元素之間留出空白。我不知道規範是什麼,但可能不允許。你可能想'ins >> num''然後使用ins.peek()來查看下一個字符是什麼。 – akton

回答

5

問題的基本要點是,如果流的狀態變壞,C++流格式化的提取操作符將停止工作,並且您必須重置狀態以使它們再次工作。

您還有其他問題。

首先,您的驗證功能顯示您缺乏經驗:static_cast<int>(intval) == intval將永遠是真實的,並確認一無所有。其次,你未能證實你確實成功地從流中提取了值(這是你的無限循環的原因:你所做的一切都是失敗的驗證一次又一次)。

所以,當你提取值,您應該驗證一切順利還好,這樣的:

int num, den; 
char symb; 
// Remember to flush unfinished lines 
std::cout << "Please enter a rational number: " << std::flush; 
if (std::cin >> num >> symb >> den) 
    // you extracted an integer, a character and an integer succesfully 
    // perhaps check that the character is '/' and denominator is non-zero 
else 
    // there was an error: what should we do? 

的「我們該怎麼辦」的部分是遠遠顯而易見的:你可以只是重置流,並從中取出第一個錯誤字節和嘗試再次,如果你認爲這是合理的(和直覺)。然而,提取可能也失敗,原因是太大的數字,在這種情況下,這可能會導致奇怪的行爲:考慮(與普通long大小的實施方案)以下輸入:

3111111111111111111111111111111111111111111111111/3 

如果你沒有得到任何實際的規格,要考慮的事情都是面向行的輸入:首先閱讀一行並嘗試解析它;如果看起來不好,請忽略它並嘗試下一個。

0

輸入流的重要概念之一是它們不受限於任何特定的設備;也就是str >> rationalstr是控制檯,當它是一個文件,當它是一個字符串,什麼時候應該工作。所以提取器應該提取。它不應該提示,也不應該解釋它不喜歡的。所有這些事情都應該在調用提取器的代碼中完成。

在提取器中進行驗證很好,但是當檢測到錯誤時,通常報告它的方式是拋出異常。你可以讓你的例外做任何你喜歡的事,持有錯誤代碼或文本字符串,或者針對不同的錯誤有不同的例外。代碼的責任是調用提取器來捕獲異常並確定適當的響應。

static_cast<int>(num) != num 

由於num是int,該static_cast沒有做任何事情。這個測試永遠不會失敗。

INT主要(){ 嘗試{ 的std ::法院< < 「請輸入一個有理數:」; Rational r; std :: cin >> r; } catch(...){ std :: cout < <「出錯了\ n」; }}

出於測試目的,我想改寫這個版本的main()使用字符串流作爲輸入源,並與"2/4""2^4"或任何我想做的與測試初始化​​;這將節省每次輸入輸入的時間。

相關問題