2010-02-16 126 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&