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

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

Кормораздатчик мобильный электрифицированный: схема и процесс работы устройства...

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

2022-10-27 32
Следующие операции перегружать  нельзя: 0.00 из 5.00 0 оценок
Заказать работу

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

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

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

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

-  sizeof, typeid

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

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

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

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

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

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

classcomplex {

intRe, Im;

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

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

complexoperator +(complexob 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){

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

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

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

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

}

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

intmain (){

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

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

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

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

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

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

}

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

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

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

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

else return false;

}

Рассмотрим подробнее функцию, переопределяющую операцию ==. Поскольку результат выполнения операции – логическая величина, то в качестве типа возвращаемого значения оператор-функции выбран bool. Функция принимает параметр типа 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 () функциями, переопределяющими стандартные операторы ввода/вывода.

classcomplex {

intRe,Im;

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

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

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

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

friendistream&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; // выводрезультата

}

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

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

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

classdynamicArray {

intlenght; // размер

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

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

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

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

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

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

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

dynamicArray&operator=(dynamicArray&obj);

};

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

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

a=newint[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 (inti =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){ // если длина неодинаковая, то изменяем длину

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

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

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";

}

returna [ 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.  В чем проблема присваивания динамических переменных?Раскройте суть понятий deepcopy и shadowcopy.

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++ компилятор генерирует версию кода для каждого типа данных, используемого в программе, в процессе, известном как конкретизации шаблона. Когда компилятор создает конкретную версию функции для заданного типа данных, говорят, что он создал порожденную функцию (generatedfunction). Процесс генерации порожденной функции называют созданием экземпляра (instantiating) функции. Другими словами порожденная функция – это конкретный экземпляр фунции шаблона. Это не повышает скорость выполнения, не улучшает использование памяти, но повышает производительность разработки программного кода. Приведенный выше код может работать не только с базовыми тапами данных, но и с массивами и объектами.

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

#include <iostream>

using namespace std;

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

template<typenameT>

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 <>

voidmySwap<int>(int&a, int&b);// явноеобъявлениефункциидлятипа int

int main() {

doubled 1 = 1, d 2 = 2;

mySwap (d 1, d 2); // используется шаблон

int i1 = 1, i2 = 2;

mySwap(i1, i2); // используется реализация

}

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 ]; // непосредственно массив

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

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

voidinput (); // ввод массива

voidoutput (); // ввод массива

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

};

template<classtypeArray>voidtArray<typeArray>::input(){// ввод

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

}

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

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

}

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

//реализует алгоритм сортировки массива методом пузырька

char is=1; inti; 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;

}

}

}

intmain(){

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

cout >>"Введите элементы массива"<< endl;

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

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

cout >>"Элементы массива, отсортированные по возрастанию"<< endl;

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

}

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

class fraction{

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

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

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

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

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

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

};

bool 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;// идентификатор

string surname; // фамилия

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

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

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

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

friendistream&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,....>

classClassName {...... }

Пример.

#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 использует типы данных int и double. Объект ob2 использует типы данных char и char*. В обоих случаях компилятор автоматически генерирует необходимые данные и функции в соответствии с типом данных, передаваемых конструктору в качестве аргументов.

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

template <typename T = int>

class ClassName {...... }

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

Пример.

#include <iostream>

using namespace std;

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';

}
};
intmain (){

myclass <> ob 1(10, 0.23); /*свойство i экземпляра ob 1 будетиметьтип int, установленный по умолчанию, в угловых скобках пусто*/

myclass < char > ob 2('Х', 0.23); /*свойство i экземпляра ob 2 будетиметьтип char, указанный в угловых скобках*/

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

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

 

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

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

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

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

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

 


Поделиться с друзьями:

Архитектура электронного правительства: Единая архитектура – это методологический подход при создании системы управления государства, который строится...

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

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

Биохимия спиртового брожения: Основу технологии получения пива составляет спиртовое брожение, - при котором сахар превращается...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.336 с.