Установка плавающего поля в функции оператора << для вывода и последующее его изменение

Допустим, у меня есть какой-то класс товаров с полем цена. Когда кто-то пытается вывести мой объект Merchandise, чаще всего с cout, я хочу отобразить сумму в долларах, за которую продает товар, с двумя точками после запятой. Но я не хочу постоянно менять поток. Например, если кто-то делает это:

double someNumber;  //someNumber should be displayed to 3 decimal places
Merchandise product;
/* something happens to product here */

cout << product << "And the number generated is " << someNumber << endl;

В этом случае продукт должен отображаться с двумя десятичными разрядами, но someNumber должен по-прежнему отображаться с 3, как ожидал клиент. Поэтому я не хочу постоянно изменять ostream при реализации своей функции, я просто хочу отобразить 2 десятичных знака для этого одного поля, а затем вернуть его в нормальное состояние. Как мне это сделать?

Я попытался изменить флаги ostream один раз, а затем снова изменить их так:

ostream& operator<< (ostream& os, const Merchandise& rhs) {
  int precision = os.precision();  //Get the current precision so we can change it back later
  /* I don't know how to get the current floatfield */
  os.setf(std::ios::fixed, std::ios::floatfield);  //Forces the same precision all the time
  os.precision(2);  //Forces a precision of 2

  os << rhs.price;

  os.precision(precision);
  /* I can't change the floatfield back, since I don't know how to get it or what kind of object to store it in */ 

  return os;
}

Я просто выясняю это по мере продвижения. Я не знаю, является ли это лучшим или стандартным методом для этого или нет. Если есть лучший способ сделать это, кто-нибудь может дать мне знать? Если нет, то как мне получить текущее поле с плавающей запятой для хранения в переменной, и какой объект я использую для его хранения? Благодарю.

2 ответа

Решение

precision() также возвращает старую точность при установке новой точности, так что вы можете воспользоваться этим и установить точность в строке с присваиванием:

std::streamsize precision = os.precision(2);

std::streamsize такой же как int для узкополосных потоков (потоков, которые используют char) но для лучшей практики вы должны этот тип.

Кроме того, чтобы хранить флаги, вы должны хранить флаги потока в объекте типа std::ios_base::fmtflags:

std::ios_base::fmtflags flags = os.flags(os.flags() | std::ios_base::fixed);

Затем просто измените его обратно, когда закончите.


Вы можете облегчить это, используя технику RAII, чтобы изменить настройки, когда закончите:

template <typename Stream>
class format_saver
{
public:
    format_saver(Stream& os)
        : os_(os)
        , flags(os.flags())
        , precision(os.precision())
    { }

    ~format_saver() { os_.flags(flags); os_.precision(precision); }
private:
    Stream& os_;
    std::ios_base::fmtflags flags;
    std::streamsize         precision;
};

Теперь вы можете сделать:

std::ostream& operator<<(std::ostream& os, const Merchandise& rhs)
{
    format_saver<std::ostream> _(os);
    return os << std::setprecision(2) << std::fixed << rhs.price;
}

Вы можете использовать iomanip библиотека:

 os << std::fixed << std::setprecision(2) << std::showpoint << rhs.price;

Вот простой пример:

#include <iostream>
#include <iomanip>
using namespace std;

class a
{
   public:
   double b;
   friend ostream& operator<< (ostream& os, const a& rhs);
};

ostream& operator<< (ostream& os, const a& rhs)
{
    os << fixed << setprecision(2) << showpoint << rhs.b;
}

int main() {
     a n1;
     n1.b = 2.34567;
     cout << "Your num: " << n1 << std::endl;
     n1.b = 123.399999;
     cout << "Your num: " << n1 << std::endl;
     return 0;
 }

выход

Your num: 2.35
Your num: 123.40

Так что это определенно должно работать, если вы используете C++.

Другие вопросы по тегам