Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Определение пользовательских манипуляторов




В ситуации, когда нужно повторно определять одну и ту же команду форматирования, пользовательский манипулятор обеспечивает простую запись, нечто вроде макро.

Манипулятор без параметра создается следующим образом. Определяется класс с функцией operator<<(), которая, в свою очередь, вызывает определенные функции форматирования.

 

Пример 6.7.1.

Определим класс манипулятора

class my_f{};

Перегрузим функцию-оператор operator<< для объекта класса манипулятора. Манипулятор позволит выполнить выравнивание по правому краю. По умолчанию выравнивание по левому краю.

ostream& operator<<(ostream& out,my_f)

{

out.width(12);

out.fill(‘# ’);

cout.setf(ios:: right,ios::adjustfield);

return out;

}

void main()

{my_f MF;

cout<<52.3456<<endl<<MF<<52.3456<<endl;

}

Синтаксис языка разрешает параметры без имен, если последние не используются в теле функции. В стандарте языка С++ отмечается, что использование специального параметра без имени «полезно для резервирования места в списке параметров». В дальнейшем этот параметр может быть введен в функции без изменения интерфейса, т.е. без изменения вызывающей программы. Такая возможность бывает удобной при развитии уже существующей программы за счет изменения входящих в нее функций.

Так как манипулятор–это функция, принимающая и возвращающая ссылку на поток, то лучше определить манипулятор MF так:

Пример 6.7.2..

ostream& MF(ostream& out)

{

return out<<resetiosflags(ios::right)<<setiosflags(ios::right);

}

 

Эти два примера показывают два различных подхода к созданию пользовательских манипуляторов. В первом случае мы, по сути, не создавали манипулятор, а перегрузили операцию >> для класса my_f. Во втором случае мы создали собственно функцию-манипулятор. Вызов её обеспечивает перегруженная операция << в классе ostream.

 

6.8. Пользовательские манипуляторы с параметрами

Пусть, например, необходимо создать новый манипулятор с параметрами wp(n,m), где n – ширина поля вывода, m – точность.

Пример 6.8.1.

//Класс манипулятора

class my_manip {

int n,m;

ostream&(*f)(ostream&,int,int);

public:

//конструктор

my_manip(ostream&(*F)(ostream&,int,int),int N, int M): f(F),n(N),m(M){}

//здесь f,F – указатели на функцию

friend ostream& operator<<(ostream& s,my_manip& my)

{return my.f(s,my.n,my.m);}

};

ostream& wi_pre(ostream& s,int n,int m)

{s.width(n);

s.flags(ios::fixed);

s.precision(m);

return s;

}

my_manip wp(int n,int m)

{return my_manip(wi_pre,n,m);

}

void main()

{cout<<52.3456<<endl<<wp(10,5)<<52.3456<<endl;

}

Рассмотрим этот пример подробнее. Конструктор класса my_manip имеет первый аргумент ostream&(*F)(ostream&,int,int). Здесь F – указатель на функцию, имеющую три аргумента типов ostream&, int и int и возвращающую ссылку на класс ostream. Теперь конкретную функцию можно задавать для класса my_manip через указатель при конструировании объекта этого класса. Мы задали функцию wi_pre. При выполнении cout<<wp(10,5) фактически вызывается следующая функция:

osteram& operator<<(cout,wp(10,5))

{return wi_pre(cout,10,5);}

Действительно, функция wp возвращает объект класса my_manip с передачей в его конструктор параметров: wi_pre, n и m.

Сформулируем последовательность создания пользовательского манипулятора с параметрами.

1. Определить класс (my_manip) с полями: параметры манипулятора + поле – указатель на функцию типа

ostream& (*f)(ostream&,<параметры манипулятора>);

2. Определить конструктор этого класса (my_manip) с инициализацией полей.

3. Определить в этом классе дружественную функцию – operator<<. Эта функция в качестве правого аргумента принимает объект класса (my_manip), левого аргумента (операнда) поток ostream и возвращает поток ostream как результат выполнения функции f.

4. Определить функцию типа *f(wi_pre), принимающую поток и параметры манипулятора и возвращающую поток. Эта функция выполняет форматирование.

5. Определить собственно манипулятор (wp) как функцию, принимающую параметры манипулятора и возвращающую объект my_manip, поле f которого содержит указатель на функцию wi_pre.

 

При выполнении манипулятора cout<<wp(10,5); вызывается функция wp, которая создает и возвращает объект my_manip, в поле f которого содержится указатель на функцию wi_pre.

Затем вызывается функция operator<<, которой передается объект my_manip, созданный функцией wp.

Наконец, функция operator<< выполняет функцию *f, т.е. wi_pre, и возвращает поток.

 

6.9. Использование макросов для создания манипуляторов

Для создания манипуляторов можно использовать макросы OMANIP(int), IMANIP(int), IOMANIP(int).

Эти макросы содержатся в файле <iomanip.h>

Ниже приведены примеры использования этих макросов.

а) манипулятор с одним параметром

ostream& w(ostream& out,int n)

{out.width(n);

//out<<setw(n);

return out;

}

OMANIP(int) w(int n)

{return OMANIP(int)(w,n);

}

Здесь две функции с именем w, вторая на первый взгляд кажется ненужной. Однако OMANIP(int) – это макрос, позволяющий определить манипулятор, который принимает параметры. Обнаружив в операции << манипулятор, компилятор использует вторую функцию для вызова первой.

б) манипулятор с двумя параметрами.

Проблема здесь в том, что макросы принимают только один параметр, так что для передачи нескольких параметров придется использовать структуру или класс. Например, для двух параметров:

struct Point

{

int n;

int m;

};

Перед использованием в манипуляторе Point нужно вызвать IOMANIPdeclare(Point). IOMANIPdeclare(Type) принимает только идентификатор, так что передача указателей или ссылок требует использования typedef.

#include<iostream.h>

#include<iomanip.h>

struct Point

{int x,y;};

IOMANIPdeclare(Point);

ostream& wp(ostream& out,Point p)

{out.width(p.x);

out.flags(ios::fixed);

out.precision(p.y);

return out;

}

OMANIP(Point) wp(int x,int y)

{Point p;

p.x=x;

p.y=y;

return OMANIP(Point)(wp,p);

}

 

Состояние потока

Каждый поток имеет связанное с ним состояние. Состояния потока описываются в классе ios в виде перечисления enum.

public:

enum io_state

{

goodbit,//нет ошибки 0Х00

eofbit,//конец файла 0Х01

failbit,//последняя операция не выполнилась 0Х02

badbit,//попытка использования недопустимой операции 0Х04

hardfail //фатальная ошибка 0Х08

};

Флаги, определяющие результат последней операции с объектом ios, содержатся в переменной state. Получить значение этой переменной можно с помощью функции int rdstate();

Кроме того, проверить состояние потока можно следующими функциями:

int bad(); возвращает 1, если badbit или hardfail

int eof(); возвращает, если eofbit

int fail(); возвращает, если failbit, badbit или hardfail

int good();возвращает, если goodbit

Если попытаться выполнить ввод из потока, который находится не в состоянии good(), то возвращается значение NULL. Если осуществляется чтение в переменную и происходит ошибка, то значение переменной не изменяется (предполагается, что переменная имеет стандартный тип, определенный в самом языке). Если операция >> используется для новых типов данных, то при её перегрузке необходимо предусмотреть соответствующие проверки.

Установить состояние потока в заданное значение можно функцией void clear(int s=0);

Эта функция устанавливает флаги ios::state в значение, заданное параметром s, не изменив при этом бита hardfail.

Пример 10.1.

#include<iostream.h>

#include<stdlib.h>

void main()

{int flags;

int k;

cin>>k;

flags=cin.rdstate();

if(flags) //если ошибка

if(flags& ios:badbit)

{cout<<”Ошибка\n”;

cin.clear(0);}

else{cerr<<”Эту ошибку нельзя исправить\n”;

abort();}

cout<<”Ввод без ошибок\n”;

cout<<k<<endl;

}

 

Пример 10.2.

#include<iostream.h>

#include<stdlib.h>

void main()

{int k;

cin>>k;

if(!cin)

if(cin.bad())

{cout<<”Oшибка\n”;

cin.clear(0);}

else

{cerr<<”Неисправимая ошибка\n”;

abort();}

cout<<”Ввод без ошибок\n”;

cout<<k<<endl;

}

Здесь используется перегруженная int operator!();

Результат операции – значение функции ios::fail().

Есть еще operator void*(), который возвращает 0, когда fail() возвращает 1, в противном случае возвращает указатель this на объект ios.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...