C++ 中通常的解决方案是定义操纵器
你试图格式化什么,而不是破解物理值
直接在输出点。 (一个可能的例外是
宽度,其中std::setw 可能直接有用。)因此,对于
例如,当实际输出一些东西时,你不会指定
零填充,或固定,带 2 位小数,但类似于:
std::cout << temperature(2) << theTemporature;
temperature 类似于:
class temperature
{
int myMinWidth;
public:
temperature( int minWidth )
: myMinWidth( minWidth )
{
}
friend std::ostream& operator<<( std::ostream& dest, temperature const& manip )
{
dest.setf( std::ios_base::fixed, std::ios_base::floatfield );
dest.precision( 2 );
dest.width( myMinWidth );
return dest;
}
};
有关可用的格式修改列表,请参阅
std::ios_base 的规范,以及以下字段
std::ios_base::fmtflags.
如果您要进行大量输出,您可能需要修改它
在完整的末尾恢复原始格式标志
表达。 (除宽度外的所有格式信息都是
粘性的,所以在这里强制固定格式会让你得到固定的
程序其余部分的格式,不一定是什么
你想要。)我使用以下作为我所有的基类
操纵者:
class StateSavingManip
{
public:
void operator()( std::ios& stream ) const;
protected:
StateSavingManip() : myStream( nullptr ) {}
~StateSavingManip();
private:
virtual void setState( std::ios& stream ) const = 0;
private:
mutable std::ios* myStream;
mutable std::ios::fmtflags mySavedFlags;
mutable int mySavedPrec;
mutable char mySavedFill;
};
实现:
namespace {
int getXAlloc() ;
int ourXAlloc = getXAlloc() + 1 ;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1 ;
assert( ourXAlloc != 0 ) ;
}
return ourXAlloc - 1 ;
}
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != nullptr ) {
myStream->flags( mySavedFlags ) ;
myStream->precision( mySavedPrec ) ;
myStream->fill( mySavedFill ) ;
myStream->pword( getXAlloc() ) = NULL ;
}
}
void
StateSavingManip::operator()(
std::ios& stream ) const
{
void*& backptr = stream.pword( getXAlloc() ) ;
if ( backptr == nullptr ) {
backptr = const_cast< StateSavingManip* >( this ) ;
myStream = &stream ;
mySavedFlags = stream.flags() ;
mySavedPrec = stream.precision() ;
mySavedFill = stream.fill() ;
}
setState( stream ) ;
}
注意pword 字段的使用,以确保只有第一个
临时操纵器恢复格式;析构函数将
以相反的构造顺序调用,但顺序
如果您有更多,通常不会指定构造
而不是表达式中的一个这样的操纵器。
最后:使用这种技术并非一切皆有可能:如果你
想系统地为温度附加度数符号,
没有办法这样做。在这种情况下,您需要定义
一个温度类,并为其重载<< 运算符;这
允许一切可以想象的事情(比你想象的要多得多
使用printf 样式格式实现)。