Самый очевидный вопрос: зачем использовать printf, когда другие инструменты более адаптированы? Другой вопрос, о котором часто забывают, заключается в том, что является (конечным) носителем вывода? Если текст будет напечатан на принтере или в текстовом поле в оконной системе, вам может не хватить работы. Шрифты в таких системах редко имеют фиксированную ширину, поэтому вам придется учитывать ширину отдельных символов. Для вывода на принтер я бы предложил вывести LaTeX, а затем выполнить его постобработку. Для вывода в окно библиотека, которую вы используете, вероятно, имеет какой-то табличный компонент, который будет выполнять форматирование за вас.
Если вы выводите, например, на какое-то устройство с фиксированной шириной шрифта, например, телетайп, вы можете использовать либо манипуляторы iostream, либо пользовательские типы. (Нет никакого способа сделать это чисто с printf
вам нужны потоки ввода-вывода.) Абстрактно говоря, определение таких типов, как Name
, Title
и MonitaryAmount
, является самым чистым решением. В этом случае вы просто определяете соответствующий оператор <<
для типа. Однако использование определяемого пользователем типа для имени и заголовка вместо просто std::string
может быть излишним, и подход манипулятора может быть предпочтительнее. (В очень большом приложении, где отдельные типы будут оправданы, вам, вероятно, потребуется вывод в разных контекстах, и вы захотите, чтобы манипуляторы также указывали их.)
В самом простом решении можно обойтись всего двумя манипуляторами: TextField
и MoneyField
: каждый манипулятор будет принимать ширину поля в качестве аргумента конструктора и устанавливать соответствующие поля формата в своем операторе <<
, например:
class TextField
{
int myWidth;
public:
TextField( int width ) : myWidth( width ) {}
friend std::ostream&
operator<<( std::ostream& dest, TextField const& manip )
{
dest.setf( std::ios_base::left, std::ios_base::adjustfield );
dest.fill( ' ' );
dest.width( manip.myWidth );
return dest;
}
};
а также
class MoneyField
{
int myWidth;
public:
MoneyField( int width ) : myWidth( width ) {}
friend std::ostream&
operator<<( std::ostream& dest, MoneyField const& manip )
{
dest.setf( std::ios_base::right, std::ios_base::adjustfield );
dest.setf( std::ios_base::fixed, std::ios_base::floatfield );
dest.fill( ' ' );
dest.precision( 2 );
dest.width( manip.myWidth );
return dest;
}
};
(На практике, вероятно, лучше использовать класс для денег. Например, вам понадобятся специальные правила округления для умножения; фактически, если вы рассчитываете налог, вам, вероятно, потребуется использовать какой-то десятичный тип, а не double
, чтобы соответствовать требованиям законодательства относительно того, как он рассчитывается.)
В любом случае, учитывая приведенные выше манипуляторы, вы можете написать что-то вроде:
TextField name( 15 );
TextField title( 8 );
MoneyField gross( 8 );
MoneyField tax( 6 );
MoneyField net( 8 );
for ( std::vector< Employee >::const_iterator employee = employees.begin();
employee != employees.end();
++ employee ) {
std::cout << name << employee->name()
<< title << employee->title()
<< gross << employee->salary()
<< tax << calculateTax( employee->salary() )
<< net << calculateNet( employee->salary() )
<< std::endl;
}
(Это предполагает, что вы очистили остальное, чтобы сделать его идиоматичным и удобным для сопровождения C++.)
person
James Kanze
schedule
09.02.2012
printf
? - person Mankarse   schedule 09.02.2012