假设我有这样的代码:
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 ..)?
谢谢。
你需要#include <iostream>
或#include <ios>
然后需要:
std::ios_base::fmtflags f( cout.flags() );
//Your code here...
cout.flags( f );
您可以将这些放在函数的开头和结尾,或者查看this answer如何在RAII中使用它。
Boost IO Stream State Saver似乎正是你所需要的。 :-)
基于代码段的示例:
void printHex(std::ostream& x) {
boost::io::ios_flags_saver ifs(x);
x << std::hex << 123;
}
请注意,此处提供的答案不会恢复std::cout
的完整状态。例如,即使在调用std::setfill
之后,.flags()
也会“坚持”。更好的解决方案是使用.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
我使用此答案中的示例代码创建了一个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;
}
通过一些修改使输出更具可读性:
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..
}
您可以在stdout缓冲区周围创建另一个包装器:
#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"
}
在一个功能:
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());
os.setf(std::ios::hex);
os << 123;
os.flags(os_flags);
}
正如其他人所说的那样,为了方便起见,你可以把上面的(以及.precision()
和.fill()
,但通常不是那些通常不会被修改并且更重的单词相关的东西)放在一个类中,并使其异常安全;构造函数应该接受std::ios&
。