|
Программирование >> Операторы преобразования типа
operator (std; :bas1cjstream<charT,traits>& strm. Fract1on& f) int n, d; Ввод числитепя strm n; /* Если числитепь прочитан успешно * - прочитать 7 и знаменатель */ If (strm.peekO == 7) { strm.ignoreO; strm d; else { d - 1; /* Если знаменатель равен нулю * - установить failbit как признак ошибки форматирования ввода-вывода */ 1f (d == 0) { strm.setstate(std::1os::fa1lbit); return strm; /* Если все прошло успешно. * изменить значение дроби */ 1f (strm) { f = Fraction(n.d); return strm; Ha этот раз знаменатель читается только в том случае, если за первым числом следует символ /; в противном случае по золчанию используется знаменатель, равный 1, а целое число интерпретируется как дробь. Таким образом, для целых чисел знаменатель не обязателен. Реализация также проверяет, не равен ли прочитанный знаменатель нулю. В этом случае устанавливается флаг ios base::failbit, что может привести к выдаче соответствующего исключения (см. с. 576). Разумеется, при нулевом знаменателе возможны и другие действия. Например, реализация может сама сгенерировать исключение или вообще отказаться от проверки знаменателя, чтобы исключение было сгенерировано классом Fraction. Наконец, мы проверяем состояние потока данных, и новое значение присваивается объекту дроби только в том случае, если ввод был выполнен без ошибок. Всегда выполняйте последнюю npOBepijy и изменяйте значение объекта, если чтение прошло успешно. Конечно, разумность чтения целых чисел как дробей можно поставить под сомнение. Существуют и другие нюансы, которые тоже можно исправить: например, символ \ должен следовать за числителем без разделяющих пробелов. С другой стороны, знаменателю может предшествовать произвольное количество пробелов, которые обычно игнорируются. Впрочем, это дает лишь отдаленное представление о сложностях, связанных с чтением нетривиальных структур данных. Ввод-вывод с использованием вспомогательных функций Если реализация оператора ввода-вывода требует доступа к закрытым данным объекта, то стандартные операторы должны поручить фактическую работу вспомогательным функциям классов. При помощи такого подхода можно также реализовать полиморфные чтение и запись. Примерная реализация выглядит так: class Fraction f public: virtual void prIntOn (std::ostream& strm) const: Вывод virtual void scanFrom (std;:1stream& strm): Ввод std;:ostream& operator (std:;ostream& strm. const Fractions f) f .prlntOn(strtn): return strm; std::1stream& operator (std:;1stream& strm. Fractions f) f f.scanFrom (strm); return strm; Типичный пример - непосредственный доступ к числителю и знаменателю дроби при вводе: void Fraction::scanFrom (std::istream& strm) { Прямое присваивание значений компонентов num = n; denom = d; Существует и другое решение: если кдасс не будет использоваться в качестве базового, операторы ввода-вывода можно объявить дружественными (friend) для данного класса. Однако этот подход существенно ограничивает свободу действий при наследовании. Дружественные функции не могут быть виртуальными, что является потенциальной причиной ошибочных вызовов. Например, если в аргументе оператора ввода передается ссылка на базовый класс, которая на самом деле ссылается на объект производного класса, то для нее будет вызван оператор базового класса. Для решения проблемы производные классы не должны реализовывать собственные операторы ввода-вывода. Таким образом, представленная реализация более универсальна, чем дружественные функции. Именно она может рассматриваться как стандартное решение, хотя в большинстве примеров применяются дружественные функции. Пользовательские операторы с функциями неформатированного ввода-вывода Приведенные выше реализации операторов ввода-вывода поручали основную работу готовым операторам форматированного ввода-вывода. Иначе говоря, операторы << и >> реализовьгаались в контексте соответствующих операторов более простых типов. Операторы ввода-вывода в стандартной библиотеке С++ определяются иначе. Общая схема выглядит так: сначала поток данных проходит предварительную обработку и готовится к вводу-выводу. Затем происходит собственно ввод-вывод и некоторая завершающая обработка. Используйте эту схему при определении собственных операторов ввода-вывода, тем самым будет обеспечена их логическая согласованность. В классах basic istrream и basic ostream определяется вспомогательный класс sentry. Конструктор этого класса выполняет предварительную обработку, а деструктор - соответствующие завершающие действия. Эти классы заменяют функции, использовавшиеся в предыдущих реализациях библиотеки lOStream (ipft{(), \sfxQ, opfx() и osfi{()). Новая схема обеспечивает завершающую обработку даже в том случае, если ввод-вывод будет отменен из-за исключения. Если оператор ввода-вывода использует функцию неформатированного ввода-вывода или напрямую работает с потоковым буфером, для него прежде всего необходимо сконструировать объект sentry. Последующая обработка будет зависеть от состояния этого объекта, по которому проверяется состояние потока данных. Для этой цели объект sentry обычно преобразуется к типу bool. Следовательно, операторы ввода-вывода в общем виде выглядят так: sentry se(strm): Косвенная организация предваритепьной и завершающей обработки if (se) { } Собственно работа с потоком В аргументе конструктора класс sentry получает объект strm, для которого должны выполняться предварительный и завершающий этапы обработки. Также необходимо дополнительно позаботиться о решении общих задач операторов ввода-вывода (синхронизации потоков данных, проверки нормального
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |