Следующие операции перегружать  нельзя: 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Следующие операции перегружать  нельзя:



-  :: (разрешение области видимости)

-  . (выбор компонента класса)

-  .* (выбор компонента класса через указатель на него)

-  ?: тернарный оператор

-  sizeof, typeid

У первых трех операций в правой части имя, а не значение. У тернарного оператора 3 параметра, к тому же его возвращаемое значение является l-value. Переопределять sizeof, typeid просто нет смысла.

Для встроенных типов данных перегружать операции нельзя, потому что тип – это набор данных и операций над ними. Изменяя операции, выполняемые над данными типа, меняется само определение типа. Также, нельзя изменить приоритет операции и определить новую операцию. Нельзя перегрузить одну операцию несколькими способами. Нельзя создавать новые операции и пытаться перегрузить их.

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

тип_возвращаемого_значения operator знак_операции (параметр ы _операции) {тело_оператор-функции }

Когда оператор-функция перегружает унарный оператор у нее не будет параметров. Когда перегружается бинарный оператор, у функции будет один параметр. Этот параметр получит тот объект, который будет расположен справа от знака оператора. Объект слева генерирует вызов оператор-функции и передается неявно с помощью указателя this.

Перегрузка арифметических операций. Создадим класс для работы с комплексными числами и переопределим для него операцию +.

class complex {

int Re, Im;

public: void input (); // ввод комплексного числа

void output (); // вывод комплексного числа

complex operator +(complex ob 2); // переопределение оператора +

};

void complex::input(){ // ввод

cout<<"Input Re";cin>>Re;

cout<<"Input Im";cin>>Im;

}

void complex::output(){ // вывод

cout<<Re<<"+i"<<Im;

}

Для переопределения операции + необходимо создать оператор функцию, которая назвается operator +. Поскольку в результате сложение двух комплексных чисел получается комплексное число, то тип возвращаемого значения у этой функции будет complex. Срабатывание этой оператор функции можно тракторавть как – объект первое слагаемое вызывает оператор функцию operator +, а объект второе слагаемое пойдет параметром в эту функцию. Таким образом у данной функции будет параметр ob 2 куда скопируется второе слагаемое, поскольку это комплексное число, то тип у него будет complex.

complex complex::operator +(complex ob2){

complex temp; // переменная для результата сложения чисел

temp. Re = Re + ob 2. Re; //складываем действительные части

temp. Im = Im + ob 2. Im; // складываем мнимые части

return temp; // возвращаем результат

}

В качестве параметра функция получает переменную типа complex под названием ob 2. Это число, которое стоит справа от знака +. Re и Im это внутренние переменные числа, которое стоит справа от знака +. Т.е. функция выполняет сложение двух чисел Re + iIm и ob 2. Re + iob 2. Im.

int main (){

complex A, B, C; // объявляем три переменные типа complex

A. input (); // ввод числа A

B. input (); // ввод числа B

C = A + B; // сложение двух комплексных чисел

/*объект А вызывает оператор функцию operator +, объект B идет в нее параметром, результат вычисления присваивается объекту С*/

C. output (); // вывод результата

}

Перегрузка логических операций и операций отношения. Особенность данных операций – их результат одно из двух значений true либо false.

Напишем оператор-функцию для перегрузки логической операции сравнения двух комплексных чисел для класса complex. Из математики известно, что два комплексных числа считаются равными, если равны их действительные и мнимые части.

int complex::operator ==(complex ob2){

if(Re==ob2.Re&&Im==ob2.Im) return true;

else return false;

}

Рассмотрим подробнее функцию, переопределяющую оператор ==. Это логический оператор, поэтому функция должна возвращать либо true, либо false. Поэтому тип данных выбран int. В качестве параметра функция принимает параметр типа complex с названием ob 2, т. е. это комплексное число, которое будет стоять справа от знака ==. Переменные Re и Im – это внутренние переменные комплексного числа, которое стоит слева от знака ==. Т. е. сравниваются два числа Re + Im * i и ob 2. Re + ob 2. Im * i.

Перегрузка операции ++. Пусть имеется класс, который реализует некий счетчик.

class counter {

private:

 int count;

public:

counter(int count = 0);  // Constructor

int getCount() const; // Getters

 void setCount(int count); // Setters

counter & operator++(); // ++prefix

const counter operator++(int dummy); // postfix++

friend std::ostream & operator<<(std::ostream & out, const counter & counter);

};

Префиксная функция возвращает ссылку на экземпляр класса, чтобы поддерживать каскадные изменения ++++ c как ++(++ c). Однако возвращенная ссылка может быть использована как lvalue с неожиданными операциями(например, ++ c = 8). 

Постфиксная функция возвращает константный объект по значению. Константное значение не может быть использовано как lvalue (элемент, которому можно присвоить значение), что и предотвратит каскадные изменения, такие как c ++++. Хотя это было бы расценено как (с++)++. Тем не менее, (с++) не возвращает текущий объект, а возвращает временный объект. Последующая операция ++ работает над временным объектом.

И префиксная и постфиксная функции не константные, т.к. они модифицируют значение свойства count.

Реализация класса counter:

#include "counter.h"

#include <iostream>

using namespace std;

counter::counter(int c): count(c) { }

int counter::getCount() const { return count; }

void counter::setCount(int c) { count = c; }

// ++ prefix, возвращает ссылку на текущий объект (this)

counter & Counter::operator++() {

  ++count;

  return *this;

}

// postfix ++, возвращает старое значение по значению

const counter counter::operator++(int dummy) {

counter old(*this);

++count;

return old;

}

// Перегрузка операции <<

ostream & operator<<(ostream & out, const counter & counter) {

out << counter.count;

return out;

}

Префиксная функция выполняет инкрементировние и возвращает объект по ссылке. Постфиксная функция сохраняет старое значение (создается новый экземпляр класса с помощью конструктора копирования), выполняет инкрементирование и возвращает сохраненный объект по значению. Очевидно, что постфиксная операция над объектом менее эффективна, чем префиксная, т.к. она создает временный объект. Если не каскадных операций, которые используются при выводе на экран, лучше использовать префиксные операции.

Напишем программу, чтобы продемонстрировать использование перегруженных операций:

#include "counter.h"

#include <iostream>

using namespace std;

int main() {

counter c1;

cout << c1 << endl;  // 0

cout <<++c1<< endl;  // 1

cout << c1 << endl;  // 1

cout <<c1++<< endl;  // 1

cout << c1 << endl;  // 2

cout <<++++c1<< endl; // 4

// cout <<c1++++<< endl; // error caused by const return value

}

Обратите внимание на разницу в подсчете << c1 ++ и << ++ c1. Обе префиксная и постфиксная операции работают, как и ожидалось.

 Выражение ++++ с1 разрешено и работает правильно. c1 ++++ запрещено, потому что он будет производить неправильный результат.

Перегрузка операций ввода-вывода. Пользовательская функция ввода не может быть членом класса, для которого она создана. Если оператор-функция является членом класса, то левый операнд является объектом, генерирующим вызов оператор функции. Т.е. левый операнд должен быть объектом этого класса. Однако в случае операторов ввода-вывода левый операнд – это поток, а правый – объект, который нужно вывести. Как и в предыдущем примере, оператор функцию можно сделать дружественной.

Модифицируем класс комплексных чисел, заменив функции input () и output () функциями, переопределяющими стандартные операторы ввода/вывода.

class complex {

intRe,Im;

public: complex operator+(complex ob2);// переопределение оператора +

// переопределяем оператор вывода

friend ostream &operator<<(ostream &stream, complex ob);

// переопределяем оператор ввода

friend istream &operator>>(istream&stream, complex &ob);

};

ostream&operator<<(ostream&stream, complex ob){

stream<<ob.Re<<"+i"<<ob.Im;

return stream; 

};

istream&operator>>(istream&stream, complex &ob){

cout<<endl<<"Input Re"; cin>>ob.Re;

cout<<"Input Im"; cin>>ob.Im;

return stream;

}

int main(){

complex A,B;

cin >> A >> B;// ввод чисел

cout << A <<"+"<< B <<"="<< A + B; // вывод результата

}

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

Перегрузка операции присваивания и конструктор компирования. Если в пользовательских типах данных (классах) имеются свойства для хранения значения которых использовалось динамическое выделение памяти, то необходимо самостоятельно разрабатывать методы для корректной работы с такими переменными-свойствами при выполнении операций присваивания, передаче таких объектов в функцию, а также их разрушении.

Если тип двух программных объектов одинаков, то один объект можно присвоить другому. По умолчанию, когда один объект присваивается другому, делается поразрядная копия всех данных-членов копируемого объекта (shallow copy). Особенностью операции присваивания является то, что если в объекте содержится динамически выделенная память, то при выполнении стандартной операции присваивания произойдет побитовое копирование данных одного объекта в другой, в том числе и указательных переменных, память, выделенная динамически скопирована не будет. В результате получится, что два объекта ссылаются на одну и ту же область в динамической памяти. На практике такая ситуация может возникнуть когда необходимо выполнить присваивание одного динамического массива другому, а также при использовании указателей для передачи или возвращения объектов в функцию. Поэтому необходимо перегружать операцию присваивания. В данном случае при перегрузке операции присваивания параметром функции-оператора является объект, содержащий динамически выделенную память. При передаче фактических параметров к формальным параметрам функции происходит копирование объектов, однако копирование динамической памяти не происходит. При выходе из функции срабатывает деструктор, который удаляет объект и пытается удалить динамическую память, которая одновременно является и динамической памятью для объекта в основной программе. Это приведет к повреждению объекта. Для решения проблемы нужно создать особый тип конструктора – конструктор копирования, в котором описать правила копирования, чтобы продублировать и те области памяти, на которые имеются ссылки (deep copy). Дополним созданный выше класс dynamicArray методами, позволяющими выполнять операцию присваивания для динамических массивов.

class dynamicArray {

int lenght; // размер

int * a; // указатель на массив в динамической памяти

public: dynamicArray (){ a = NULL;}; // конструктор без параметров

dynamicArray (int n); // конструктор с параметрами

dynamicArray(const dynamic Array&obj); // конструктор копирования

void output(); // вывод массива

~dynamicArray(){deletea;}; // деструктор

// переопределение оператора присваивания

dynamicArray&operator=(dynamicArray&obj);

};

dynamicArray::dynamicArray(int n){ // конструктор

lenght=n; // устанавливаем длину

a=new int[n]; // выделяем память

for(int i=0;i<lenght;i++)

a[i]=i;

}

dynamicArray::dynamicArray(const dynamicArray&obj){/*конструктор копирования*/

lenght=obj.lenght; // устанавливаем длину

a=new int[obj.lenght]; // выделяем память

//переписываем элементы массива

for (int i =0; i < obj. lenght; i ++)

a[i]=obj.a[i];

}

void dynamicArray::output(){//вывод элементов массива

cout<<endl;

for(int i=0;i<lenght;i++)

cout<<a[i]<<" ";

}

dynamicArray&dynamicArray::operator=(dynamicArray&obj){// переопределение оператора присваивания

if (lenght!= obj. lenght){ // если длина неодинаковая, то изменяем длину

delete a; // удаляем память у объекта слева от =

/*Для объекта стоящего слева от знака равно выделяем новую память размером как у объекта стоящего справа от знака =*/

a=new int[obj.lenght];

lenght = obj. lenght; // устанавливаем длину объекту слева от =

}

for (int i=0;i<lenght; i++){//переписываем значения

         a[i]=obj.a[i]; 

}

return *this;

}

int main(){

dynamicArrayA(5),B; // создаем два массива

cout<<endl<<"Array A";

A.output(); // выводим массив A

B = A; // присваиваем массиву B массив A

cout << endl <<" ArrayB ";

B. output (); // выводим массив B

cout << B [1];

}

Объект (переменная типа класса) можно использовать в качестве аргументов функции. Объекты передаются в функции по значению, то есть в функцию передается не сам объект, а его копия. Это значит, что любые изменения, происходящие с элементами объекта внутри функции, не изменяют самого объекта. Для того, чтобы данные класса dynamicArrayизменились в основной программе необходимо передать в функцию не копию объекта, а сам объект. Для этого передается ссылка на объект (адрес этого объекта). В приведенном примере объект передается по ссылке в конструкторе копирования следующим образом: dynamicArray (constdynamicArray & obj);.

Во время выполнения операции присваивания сначала вызывается функция, переопределяющая оператор «=» из класса dynamicArray, которая в свою очередь для передачи параметров использует конструктор копирования.

Перегрузка операции []. Для того чтобы к элементам динамического массива можно было обращаться через индексированные имена, необходимо перегрузить операцию []. Необходимо разработать 2 версии оператор-функции: одна для чтения значений, другая для записи. Версия для чтения объявляется как константная член-функция, в то время как версия для записи возвращает ссылку на элемент, которая может быть использована как lvalue для присваивания.

// версия оператор-функции для чтения значений

int dynamicArray::operator[] (int index) const {

if (index < 0 || index >= lenght) {

cout<<"error: index out of range";

}

return a [ index ];

}

// версия оператор-функции для записи a [ i ] = x

int &dynamicArray::operator[] (int index) {

if (index < 0 || index >= lenght) {

cout<<"error: index out of range";

}

return a[index];

}

Рассмотрим еще один интересный вариант применения перегруженной операции []. Выполнив данную перегрузку, можно обращаться к членам класса по индексным именам, как к элементам массива, в порядке их объявления.

#include<iostream.h>

#include<conio.h>

#include<string.h>

class ThreePart{

int a,b,c; // размер

public: ThreePart(){a=1;b=2;c=3;}

int operator[] (int index) const;

int & operator[] (int index);

};

// Indexing operator - Read

int ThreePart::operator[] (int index) const {

if (index < 0 || index >= 3) {

cout<<"error: index out of range";

}

return *(& a + index);

}

Выражение *(& a + index) выполняется следующим образом. Берется адрес переменной a, к нему добавляется заданное в переменной индекс количество единиц и выводится значение по адресу.

// Indexing operator - Writable a[i] = x

int & ThreePart::operator[] (int index) {

if (index < 0 || index >= 3) {

cout<<"error: index out of range";

}

return *(&a+index);

}

int main(){

ThreePart A,B; // создаем два массива

cout<<endl<<"Array A";

//A.output(); // выводиммассив A

B=A; // присваиваем массиву B массив A

cout<<endl<<"ArrayB";

//B.output(); // выводим массив B

cout<<B[1];

   getch();

}

 

Контрольные вопросы

1.  Что подразумевает под собой механизм перегрузки операций?

2.  Как перегрузить бинарный и унарный оператор?

3.  Переопределить операции * и + для класса fraction. Обратите внимание, что придется два раза перегружать оператор *: один раз для умножения на целое число, а другой раз для умножения дроби на дробь.

4.  Для чего используются дружественные функции?

5.  В чем особенность переопределения операций ввода-вывода?

6.  Перегрузить операции ввода и вывода для классов fraction, ground и blockFence, а также операцию вывода для класса dinamicArray.

7.  В чем проблема присваивания динамических переменных? Раскройте суть понятий deep copy и shadow copy.

8.  Для чего используется конструктор копирования? Приведите пример.

 

Шаблоны

 

Шаблон функций

 

Функция-шаблон реализует некий алгоритм, который применяется к различным типам данных. Тип данных функция-шаблон получает в качестве параметра. Синтаксис объявления шаблона:

template <typename T> OR template <class T>

return-type function-name(function-parameter-list) {...... }

Пример.

#include <iostream>

using namespace std;

/*шаблон функции, которая меняет местами значения двух переменных, переданных по ссылке, тип переменных-аргументов – определяет параметр шаблона*/

template <typename T>

void mySwap(T &a, T &b);

int main() {

int i1 = 1, i2 = 2;

mySwap(i1, i2); // компилятор генерирует mySwap(int &, int &)

cout << "i1 is " << i1 << ", i2 is " << i2 << endl;

char c1 = 'a', c2 = 'b';

mySwap(c1, c2); // компилятор генерирует mySwap(char &, char &)

cout << "c1 is " << c1 << ", c2 is " << c2 << endl;

double d1 = 1.1, d2 = 2.2;

mySwap(d1, d2); // Compiler generates mySwap(double &, double &)

cout << "d1 is " << d1 << ", d2 is " << d2 << endl;

// mySwap(i1, d1);

// error: no matching function for call to 'mySwap(int&, double&)'

// note: candidate is:

// note: template<class T> void mySwap(T&, T&)

}

template <typename T>

void mySwap(T &a, T &b) {

T temp;

temp = a;

a = b;

b = temp;

}

C++ компилятор генерирует версию кода для каждого типа, используемого в программе, в процессе, известном как конкретизации шаблона. Когда компилятор создает конкретную версию функции для заданного типа данных, говорят, что он создал порожденную функцию (generated function). Процесс генерации порожденной функции называют созданием экземпляра (instantiating) функции. Другими словами порожденная функция – это конкретный экземпляр фунции шаблона. Это не повышает скорость выполнения, не улучшает использование памяти, но повышает производительность разработки программного кода. Приведенный выше код может работать не только с базовыми тапами данных, но и с массивами и объектами.

Функции-шаблоны могут быть перегруженными.

#include <iostream>

using namespace std;

template <typename T>

void mySwap(T &a, T &b);

// замена местами значение двух переменных для базовых типов

template <typename T>

void mySwap(T a[], T b[], int size);

/* замена местами значение элементов массивов, тип которых является параметром шаблона */

template <typename T>

void print(const T * const array, int size);

/*вывод на экран элементов массива, тип которого является параметром шаблона */

int main() {

int i1 = 1, i2 = 2;

mySwap(i1, i2); // Компилятор генерирует mySwap(int &, int &)

cout << "i1 is " << i1 << ", i2 is " << i2 << endl;

const int SIZE = 3;

int ar1[] = {1, 2, 3}, ar2[] = {4, 5, 6};

mySwap(ar1, ar2, SIZE);

print(ar1, SIZE);

print(ar2, SIZE);

}

template <typename T>

void mySwap(T &a, T &b) {

T temp;

temp = a;

a = b;

b = temp;

}

template <typename T>

void mySwap(T a[], T b[], int size) {

T temp;

for (int i = 0; i < size; ++i) {

temp = a[i];

a[i] = b[i];

b[i] = temp;

}

}

template <typename T>

void print(const T * const array, int size) {

cout << "(";

for (int i = 0; i < size; ++i) {

cout << array[i];

if (i < size - 1) cout << ",";

}

cout << ")" << endl;

}

В приведенном выше примере содержится два варианта функции-шаблона для замены местами целых типов данных, а также для замены местами массивов.

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

#include <iostream>

using namespace std;

template <typename T>

void mySwap(T &a, T &b); // Template

template <>

void mySwap<int>(int &a, int &b);

//явное объявление функции для типа int

int main() {

double d1 = 1, d2 = 2;

mySwap(d1, d2); // use template

int i1 = 1, i2 = 2;

mySwap(i1, i2); // use specialization

}

template <typename T>

void mySwap(T &a, T &b) {

cout << "Template" << endl;

T temp;

temp = a;

a = b;

b = temp;

}

template <>

void mySwap<int>(int &a, int &b) {

cout << "Specialization" << endl;

int temp;

temp = a;

a = b;

b = temp;

}

 

Контрольные вопросы и задания

1. В чем суть технологии программирования с использованием шаблонов? Каковы ее преимущества и недостатки?

2. Приведите синтаксис объявления шаблона функции.

 

Шаблоны классов

 

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

template < class nmun > class имя_класса {

}

Здесь nmun является параметром-типом, который будет указан при создании объекта класса. При необходимости можно определить несколько типов, разделяя их запятыми.

После создания класса-шаблона можно создать конкретный объект этого класса, используя следующую общую форму:

имя_класса<тип> объект;

Тип – имя типа данных, с которым будет оперировать этот класс.

Функции-элементы класса-шаблона являются сами по себе автоматически шаблонами. Нет необходимости особым образом указывать на то, что они являются шаблонами с использованием ключевого слова template.

В следующей программе создается класс-шаблон массив, будет содержать три метода: ввод массива, вывод и сортировку его методом пузырька.

#define SIZE 10 // максимальныйразмермассива

template<class typeArray> class tArray{

typeArrayB [ SIZE ]; // непосредственно массив

int n; // реальный размер массива

public: tArray (intl){ n = l;};//конструктор, инициализирует реальный размер

void input (); // ввод массива

void output (); // ввод массива

void bubble (); // сортировка методом пузырька

};

template<class typeArray>void tArray<typeArray>::input(){// ввод

for (int i=0;i<n;i++){cin>>B[i];}

}

template<class typeArray> void tArray<typeArray>::output(){//вывод

for (int i=0;i<n;i++){cout<<B[i];}

}

template<class typeArray> void tArray<typeArray>::bubble(){

char is=1; int i; typeArray c;

while(is){

is=0;

for (i=1; i<n; i++)

          if (B[i] < B[i-1]){

c=B[i];

B[i]=B[i-1];

B[i-1]=c; is=1;

}

}

}

int main(){

tArray < int > C (3); // создаем массив типа int из трех элементов

C. input (); // ввод массива

C. bubble (); // сортировка методом пузырька

C. output (); // вывод отсортированного массива

}

Данный класс-шаблон можем использовать и для работы с массивом простых дробей, для обработки которых используется определенный выше класс fraction. Только класс fraction необходимо немного модифицировать: переопределить операции ввода и вывода, а также оператор <, чтобы была возможность применить операцию сортировки методом пузырька.

class fraction{

int m; // числитель

int n; // знаменатель

public: intoperator <(fractionob 2); // переопределение оператора<

// переопределение операторов ввода / вывода

friend istream&operator>>(istream&stream, fraction &ob);

friend ostream&operator<<(ostream&stream, fraction ob);

};

int fraction::operator <(fraction ob2){

if(m*ob2.n<n*ob2.m) return true;

else return false;

}

Рассмотрим подробнее функцию, переопределяющую оператор <. Это логический оператор, поэтому функция должна возвращать либо true, либо false. Поэтому тип данных выбран int. В качестве параметра функция принимает параметр типа fraction с названием ob 2, т. е. это дробь, которая будет стоять справа от знака <. Переменные m и n – это внутренние переменные дроби, которая стоит слева от знака <. Т. е. сравниваются две дроби m / n и ob 2. m / ob 2. n. Для сравнения дробей мы должны привести их к общему знаменателю и сравнить числители. Если дробь слева от оператора меньше дроби справа, то возвращаем истину, иначе возвращаем ложь.

istream&operator>>(istream&stream, fraction &ob){

cout<< "Числитель"; stream >>ob.m;

cout<< " Знаменатель "; stream >>ob.n;

return stream;

}

ostream&operator<<(ostream&stream, fraction ob){

stream<<ob.m<<"/"<<ob.n;

return stream;

};

int main(){

tArray<fraction>C(3); /* создаем массив типа fraction из 3- х элементов */

C. input (); // ввод массива

C. bubble (); // сортировка методом пузырька

C. output (); // вывод отсортированного массива

}

Как видим, программный код в главной функции отличается от кода из примера с массивом целых чисел только в первой строке при указании типа массива. Более того, данный класс-шаблон подойдет и для решения более сложных практических задач. Например, необходимо отсортировать массив, содержащий результаты выступления спортсменов в порядке возрастания набранных баллов. Для решения необходимо всего лишь создать класс sportsmen, определяющий обработку данных о конкретном спортсмене, в котором переопределить операции ввода, вывода и операцию <. В этом случае для класса подойдут все методы, определенный в классе-шаблоне tArray.

class sportsmen{

int id;// идентификатор

char surname[10]; // фамилия

int result; // результат

public: int operator<(sportsmen ob2);// переопределение оператора <

// переопределяем операторы ввода и вывода

friend ostream&operator<<(ostream&stream, sportsmen ob);

friend istream&operator>>(istream&stream, sportsmen &ob);

};

int sportsmen::operator <(sportsmen ob2){

if(result<ob2.result) return true;

else return false;

}

ostream&operator<<(ostream&stream, sportsmen ob){

cout<<ob.id<<"/"<<ob.surname<<"/"<<ob.result<<endl;

return stream;

}

istream&operator>>(istream&stream, sportsmen &ob){

cout<<endl<<"Input id"; cin>>ob.id;

cout<<"Input Surname"; cin>>ob.surname;

cout<<"Input result"; cin>>ob.result;

return stream;

}

int main(){

tArray<sportsmen>C(3);// создаем массивтипа sportsmen

C. input ();//вводмассива

C. bubble ();//сортировка методом пузырька

C. output ();//вывод отсортированного массива

}

Как видим программный код внутри функции main () отличается от предыдущих примеров только типом используемого массива. Но программа выполняет действия по обработке данных о спортсменах.

Шаблон это не класс. Это инструкция компилятору, как сгенерировать определение класса. Весь код шаблона должен содержаться в одном файле с расширением.h, который подключается с помощью директивы #include ко всем файлам, где он используется. Компилировать отдельно шаблон нельзя. C ++ компилятор создает класс для каждого из параметризованных типов, которые используются в программе.

Класс-шаблон может содержать несколько параметрированных типов:

template <typename T1, typename T2,....>

class ClassName {...... }

Пример.

#include <iostream.h>
template <class Type1, class Type2> class myclass{
Type1 i;
Type2 j;
public:

myclass (Type1 a, Type2 b) { i = a; j = b; }

void show () { cout << i << ' ' << j << '\n'; }
};
int main(){

myclass<int, double> ob1(10, 0.23);

myclass<char, char *> ob2(' Х ', "This is a test");

ob1.show(); // вывод int, double

ob2.show(); // вывод char, char *
}

Эта программа выдаст следующий результат на экран:
10 0.23
X This is a test

В программе объявляются два типа объектов. Объект ob1 использует типы данных integer и double. Объект ob2 использует типы данных char и char*. В обоих случаях компилятор автоматически генерирует необходимые данные и функции в соответствии с типом данных, передаваемых конст­руктору в качестве аргументов.

Можно также задать тип по умолчанию:

template <typename T = int>

class ClassName {...... }

Чтобы создать экземпляр класса с типом по умолчанию используется конструкция: ClassName <>.

Пример.

#include <iostream.h>
template <class Type1=int> class myclass{
Type1 i;
double j;
public:

myclass (Type1 a, double b) { i = a; j = b; }

void show () { cout << i << ' ' << j << '\n'; }
};
int main(){

myclass<int> ob1(10, 0.23);

myclass<char, char *> ob2(' Х ', 0.23);

ob1.show(); // вывод int

ob2.show(); // вывод char
return 0;
}

 

Контрольные вопросы и задания

1.  Что подразумевает под собой технология шаблонов?

2.  Как объявить и как использовать шаблон класса?

3.  Добавить в шаблон метод добавления элемента в массив.

4.  Добавить в шаблон метод удаления i-го элемента массива.

 



Поделиться:


Последнее изменение этой страницы: 2019-05-20; просмотров: 580; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 54.225.35.224 (0.365 с.)