Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
недостающая теория.doc
Скачиваний:
8
Добавлен:
22.09.2019
Размер:
619.01 Кб
Скачать

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.