2010-02-16 45 views
57

假設我有這樣的代碼:恢復性病的狀態::法院它

void printHex(std::ostream& x){ 
    x<<std::hex<<123; 
} 
.. 
int main(){ 
    std::cout<<100; // prints 100 base 10 
    printHex(std::cout); //prints 123 in hex 
    std::cout<<73; //problem! prints 73 in hex.. 
} 

我的問題是,如果有任何的方式來後COUT的狀態「恢復」到原來的一個從函數返回? (有點像std :: boolalpha和std :: noboolalpha ..)?

謝謝。

+0

我相信十六進制只能用於下一個移出操作。如果您手動更改格式標誌而不是使用操縱器,則更改僅保留。 – 2010-02-16 15:39:20

+2

@BillyONeal:不,使用操縱器與手動更改格式標誌具有相同的效果。 :-P – 2010-02-16 15:55:45

+2

如果您在此處由於Covertiy發現***不恢復ostream格式(STREAM_FORMAT_STATE)***,請參閱[Coverity finding:Not restoring ostream format(STREAM_FORMAT_STATE)](http://stackoverflow.com/ q/34503914)。 – jww 2016-01-25 21:11:29

回答

57

谷歌搜索給了我這個:

ios::fmtflags f(cout.flags()); 

    //Your code here... 

    cout.flags(f); 

你可能希望把他們在你的函數的開始和結束。

+9

Yuck ,那些不知道C++風格的人,就是那個應該直接初始化變量---'ios :: fmtflags f(cout.flags())---而不是兩步「初始化「如上所示。 – 2010-02-16 13:59:31

+0

對於我的原始副本和從互聯網上粘貼,我表示歉意。但我承認,我經常不使用C++。如果您認爲它更加語義化,請隨意編輯。 – 2010-02-16 14:08:02

+0

謝謝!這可能是我一直在尋找的! – SuperSaiyan 2010-02-16 14:09:01

47

Boost IO Stream State Saver看起來正是你所需要的。 :-)

例如基於您的代碼段:

void printHex(std::ostream& x) { 
    boost::io::ios_flags_saver ifs(x); 
    x << std::hex << 123; 
} 
+0

請注意,這裏沒有什麼魔法,'ios_flags_saver'基本上只是保存並設置標誌,就像@ StefanKendall的回答。 – einpoklum 2016-02-28 13:10:42

+10

@einpoklum但與其他答案不同,它是異常安全的。 ;-) – 2016-02-28 14:46:15

+1

是的,沒錯,這很重要。 – einpoklum 2016-02-28 14:52:10

7

隨着修改一點點,使輸出更具可讀性:

void printHex(std::ostream& x) { 
    ios::fmtflags f(x.flags()); 
    x << std::hex << 123 << "\n"; 
    x.flags(f); 
} 

int main() { 
    std::cout << 100 << "\n"; // prints 100 base 10 
    printHex(std::cout);  // prints 123 in hex 
    std::cout << 73 << "\n"; // problem! prints 73 in hex.. 
} 
+0

對空函數使用'(void)'是不常用的C++風格;不像C,在C++中'()'永遠是空的。但是,是的,我承認嚴格閱讀C++標準確實需要編寫「int main(void)」,並且「int main()」不符合要求。好吧。 :-P – 2010-02-16 14:13:27

+0

@Chris Jester-Young:謝謝:) – 2010-02-16 14:14:22

+2

@ ChrisJester-Young:我剛剛檢查了ISO C標準的1998,2003和2011版。所有指定'int main()',沒有'void'關鍵字作爲允許的形式之一。 – 2013-11-04 16:25:20

12

我使用示例代碼從這個答案創建了一個RAII類。如果你在一個設置iostream標誌的函數中有多個返回路徑,這種技術的最大優點就來了。無論使用哪條返回路徑,析構函數將始終被調用,並且標誌將始終得到重置。當函數返回時,不會忘記恢復標誌。

class IosFlagSaver { 
public: 
    explicit IosFlagSaver(std::ostream& _ios): 
     ios(_ios), 
     f(_ios.flags()) { 
    } 
    ~IosFlagSaver() { 
     ios.flags(f); 
    } 

    IosFlagSaver(const IosFlagSaver &rhs) = delete; 
    IosFlagSaver& operator= (const IosFlagSaver& rhs) = delete; 

private: 
    std::ostream& ios; 
    std::ios::fmtflags f; 
}; 

您將通過創建IosFlagSaver的本地實例,每當你想保存當前位狀態,然後使用它。當這個實例超出範圍時,標誌狀態將被恢復。

void f(int i) { 
    IosFlagSaver iosfs(std::cout); 

    std::cout << i << " " << std::hex << i << " "; 
    if (i < 100) { 
     std::cout << std::endl; 
     return; 
    } 
    std::cout << std::oct << i << std::endl; 
} 
+2

非常好,如果有人拋出,你仍然在你的流中得到正確的標誌。 – 2015-02-16 04:32:51

+3

除了標誌,還有更多的流狀態。 – jww 2017-03-18 11:59:31

+1

我真的希望C++允許try/finally。這是RAII工作的一個很好的例子,但最後會更簡單。 – 2017-04-18 17:28:33

24

請注意,此處顯示的答案不會恢復std::cout的完整狀態。例如,即使在致電.flags()後,std::setfill也會「粘住」。一個更好的解決方案是使用.copyfmt

std::ios oldState(nullptr); 
oldState.copyfmt(std::cout); 

std::cout 
    << std::hex 
    << std::setw(8) 
    << std::setfill('0') 
    << 0xDECEA5ED 
    << std::endl; 

std::cout.copyfmt(oldState); 

std::cout 
    << std::setw(15) 
    << std::left 
    << "case closed" 
    << std::endl; 

會打印:

case closed 

而不是:

case closed0000 
+0

儘管我原來的問題已經在幾年前回答了,但這個答案是一個很好的補充。 :-) – SuperSaiyan 2015-06-22 07:49:58

2

就可以圍繞標準輸出緩衝區中的另一包裝:

#include <iostream> 
#include <iomanip> 
int main() { 
    int x = 76; 
    std::ostream hexcout (std::cout.rdbuf()); 
    hexcout << std::hex; 
    std::cout << x << "\n"; // still "76" 
    hexcout << x << "\n"; // "4c" 
} 

在func重刑:

void print(std::ostream& os) { 
    std::ostream copy (os.rdbuf()); 
    copy << std::hex; 
    copy << 123; 
} 

當然,如果性能是一個問題,這是一個有點貴,因爲它是複製整個ios對象(但不包括緩衝區)在內的一些東西,你付出的,但不太可能使用如語言環境。

否則我覺得如果你打算使用.flags(),最好保持一致並使用.setf()而不是<<語法(風格的純粹問題)。

void print(std::ostream& os) { 
    std::ios::fmtflags os_flags (os.flags()); 
    std::size_t os_width (os.width()); 
    os.setf(std::ios::hex); 
    os.width(4); 

    os << 123; 

    os.flags(os_flags); 
    os.width(os_width); 
} 

正如其他人說你可以把上面的(和.precision().fill(),但通常沒有語言環境和單詞相關的東西,通常不會被修改,並且較重)一類爲方便和使其異常安全;構造函數應該接受std::ios&