- •2.5. Простейший ввод и вывод
- •2.5.1. Объект cin
- •2.5.2. Объект cout
- •2.5.3. Манипуляторы
- •2.6. Операторы для динамического выделения и освобождения памяти (new и delete)
- •5. Перегрузка
- •5.1. Перегрузка функций
- •5.2. Перегрузка операторов
- •5.2.1. Перегрузка бинарного оператора
- •5.2.2. Перегрузка унарного оператора
- •5.2.3. Дружественная функция operator
- •5.2.5. Перегрузка оператора []
- •5.2.6. Перегрузка оператора ()
- •5.2.8. Перегрузка операторов new и delete
- •6. Шаблоны
- •6.1. Параметризированные классы
- •6.2. Передача в шаблон класса дополнительных параметров
- •6.3. Шаблоны функций
- •6.4. Совместное использование шаблонов и наследования
- •6.5. Шаблоны класса и friend-функции
- •7.1. Организация ввода-вывода
- •7.2. Состояние потока
- •7.3. Строковые потоки
- •7.4. Организация работы с файлами
- •7.5. Организация файла последовательного доступа
- •Istream& seekg( streampos pos );
- •Istream& seekg( streamoff off, ios::seek_dir dir );
- •7.6. Создание файла произвольного доступа
- •7.7. Основные функции классов ios, istream, ostream
- •8.1. Основы обработки исключительных ситуаций
- •8.2. Перенаправление исключительных ситуаций
- •8.3. Исключительная ситуация, генерируемая оператором new
- •8.4 Генерация исключений в конструкторах
- •8.5. Задание собственной функции завершения
- •8.6. Спецификации исключительных ситуаций
- •8.7. Задание собственного неожиданного обработчика
- •8.8. Иерархия исключений стандартной библиотеки
6.4. Совместное использование шаблонов и наследования
Шаблонные классы, как и обычные, могут использоваться повторно. Шаблоны и наследование представляют собой механизмы повторного использования кода и могут включать полиморфизм. Шаблоны и наследования связаны между собой следующим образом:
шаблон класса может быть порожден от обычного класса;
шаблонный класс может быть производным от шаблонного класса;
обычный класс может быть производным от шаблона класса.
Ниже приведен пример простой программы, демонстрирующей наследование шаблонного класса oper от шаблонного класса vect.
#include <iostream>
using namespace std;
template <class T>
class vect // класс-вектор
{protected:
T *ms; // массив-вектор
int size; // размерность массива-вектора
public:
vect(int n) : size(n) // конструктор
{ ms=new T[size];}
~vect(){delete [] ms;} // деструктор
T &operator[](const int ind) // доопределение операции []
{ if((ind>0) && (ind<size)) return ms[ind];
else return ms[0];
}
};
template <class T>
class oper : public vect<T> // класс операций над вектором
{ public:
oper(int n): vect<T>(n) {} // конструктор
~oper(){} // деструктор
void print() // функция вывода содержимого вектора
{ for(int i=0;i<size;i++)
cout<<ms[i]<<' ';
cout<<endl;
}
};
int main()
{ oper <int> v_i(4); // int-вектор
oper <double> v_d(4); // double-вектор
v_i[0]=5; v_i[1]=3; v_i[2]=2; v_i[3]=4; // инициализация int
v_d[0]=1.3; v_d[1]=5.1; v_d[2]=.5; v_d[3]=3.5; // инициализация double
cout<<"int вектор = ";
v_i.print();
cout<<"double вектор = ";
v_d.print();
return 0;
}
Как следует из примера, реализация производного класса от класса-шаб лона в основном ничем не отличается от обычного наследования.
6.5. Шаблоны класса и friend-функции
Для шаблонов класса, как и для обычных классов, могут быть установлены отношения дружественности. Дружественность может быть установлена между шаблонным классом и глобальной функцией, функцией-членом другого (возможно шаблонного класса) или целым классом (возможно шаблонным).
7. ПОТОКИ ВВОДА-ВЫВОДА В С++
7.1. Организация ввода-вывода
Системы ввода−вывода С и С++ основываются на понятии потока. Поток в С++ − это абстрактное понятие, относящееся к переносу информации от источника к приемнику. В языке С++ реализованы две иерархии классов, обеспечивающих операции ввода−вывода, базовыми классами которых являются streambuf и ios [1,2,3]. На рис. 7 приведена диаграмма классов, базовым для которых является ios.
В С++ используется достаточно гибкий способ выполнения операций ввода−вывода классов с помощью перегрузки операторов << (вывода) и >> (ввода). Операторы, перегружающие эти операции, обычно называют инсертером и экстрактором. Для обеспечения работы с потоками ввода−вывода необходимо включить файл iostream, содержащий класс iostream. Этот класс является производным от ряда классов, таких как ostream, обеспечивающий вывод данных в поток, и istream − соответственно чтения из потока.
Приводимый ниже пример показывает, как можно перегрузить оператор ввода-вывода для произвольных классов.
#include <iostream>
using namespace std;
class cls
{ char c;
short i;
public :
cls(char C,short I ) : c(C), i(I){}
~cls(){}
friend ostream &operator<<(ostream &,const cls);
friend istream &operator>>(istream &,cls &);
};
ostream &operator<<(ostream &out,const cls obj)
{ out << obj.c<<obj.i << endl;
return out;
}
istream &operator>>(istream &in,cls &obj)
{ in >> obj.c>>obj.i;
return in;
}
int main()
{ cls s(’a’,10),ss(’ ’,0);
cout<<"abc"<<endl;
cout<<s<<ss<<endl;
cin >> ss;
return 0;
}
Общая форма функции перегрузки оператора ввода−вывода имеет вид:
istream &operator>>(istream &поток,имя_класса &объект)
ostream &operator<<(ostream &поток,const имя_класса объект)
Как видно, функция принимает в качестве аргумента и возвращает ссылку на поток. В результате доопределенный оператор можно применить последовательно к нескольким операндам
cout <<s<<ss;
Это соответствует
(cout.operator<<(a)).operator<<(b);
В приведенном примере функция operator не является компонентом класса cls, так как левым аргументом (в списке параметров) такой функции должен быть this-указатель для объекта, инициирующего перегрузку. Доступ к private-данным класса cls осуществляется через friend-функцию operator этого класса.
Рассмотрим использование перегрузки операторов << и >> для определения новых манипуляторов. Ранее мы рассмотрели использование стандартных манипуляторов форматирования выводимой информации. Однако можно определить и новые манипуляторы без изменения стандартных. В качестве примера рассмотрим переопределение манипулятора с параметрами, задающего для выводимого дробного числа ширину поля и точность.
#include<iostream>
using namespace std;
class manip
{ int n,m;
ostream & (*f)(ostream&,int,int) ;
public:
manip(ostream& (*F)(ostream&,int,int), int N, int M) :
f(F), n(N), m(M) {}
friend ostream& operator<<(ostream& s, const manip& obj)
{return obj.f(s,obj.n,obj.m);}
};
ostream& f_man(ostream & s,int n,int m)
{ s.width(n);
s.flags(ios::fixed);
s.precision(m);
return s;
}
manip wp(int n,int m)
{ return manip(f_man,n,m);
}
int main()
{ cout<< 2.3456 << endl;
cout<<wp(8,1)<<2.3456 << endl;
return 0;
}
Компонента-функция put и вывод символов
Компонента-функция ostream::put() используется для вывода одиночного символа:
char c=’a’;
. . .
cout.put(c);
Вызовы функции put() могут быть сцеплены:
cout.put(c).put('b').put('\n');
в этом случае на экран выведется буква а, затем b и далее символ новой строки.
Компоненты-функции get и getline для ввода символов.
Функция istream::get() может быть использована в нескольких вариантах.
Первый вариант – функция используется без аргументов. Вводит из соответствующего потока одиночный символ и возвращает его значение. Если из потока прочитан признак конца файла, то get возвращает EOF.
#include<iostream>
using namespace std;
int main()
{ char c;
cout << cin.eof()<< " вводите текст" << endl;
while((c=cin.get())!=EOF)
cout.put(c);
cout << endl<<cin.eof();
return 0;
}
В программе считывается из потока cin очередной символ и выводится с помощью функции put. При считывании признака конца файла (Ctrl+z) завершается цикл while. До начала цикла выводится значение, возвращаемое функцией cin.eof(), равное false (выводится 0). После окончания цикла выводится значение true (выводится 1).
Второй вариант – когда функция get() используется с одним символьным аргументом. Функция возвращает false при считывании признака конца файла, иначе − ссылку на объект класса istream, для которого вызывалась функция get.
. . .
while(сin.get(с))
cout.put(c);
. . .
При третьем варианте функция get() принимает три параметра: указатель на символьный массив (строку), максимальное число символов и ограничитель ввода (по умолчанию '\n'). Ввод прекращается, когда считано число символов на один меньшее максимального или считан символ-ограничитель. При этом во вводимую строку добавляется нуль-символ. Символ-ограничитель из входного потока не удаляется, это при повторном вызове функции get приведет к формированию пустой строки.
сhar s[30];
. . .
сin.get(s,20)) // аналогично cin.get(s,20, '\n')
cout<<s<<endl;
. . .
Функция isteram::getline() действует аналогично функции get() с тремя параметрами с тем отличием, что символ-ограничитель удаляется из входного потока.
Ниже коротко рассмотрены другие функции-компоненты класса istream.