Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Тип  operator знак_операции (список_параметров);




 

Перечислим некоторые правила перегрузки операций:

· можно перегружать любые операции С++, за исключением «.», «.*», «?:», «::», sizeof;

· не допускается перегрузка операций для стандартных (определенных в С++) типов данных;

· нельзя изменять синтаксис операций. Например, операция ++ перегружается как унарная (имеющая один операнд), операция + может переружаться как унарная, так и как бинарная;

· функции–операции не могут иметь параметры со значениями по умолчанию;

· при перегрузке бинарной операции перегружаемая функция, как метод класса, должна иметь один параметр. Вторым является неявный параметр – константный указатель this на текущий объект(левый операнд);

· при перегрузке бинарных операций: + - * / % ++ -- в большинстве случаев перегружаемая функция возвращает объект класса;

· чтобы сохранить семантику присваивания, операция = должна перегружаться как функция, принимающая один параметр – ссылку на присваиваемый объект класса, и возвращающая ссылку на объект класса, для которого она вызвана;

· операция индексирования должна перегружаться как функция, возвращаящая  ссылку на элемент из множества значений.

 

 

Пример 4_2. Класс «Вектор1» с конструкторами и перегрузкой операций

Файл vector 1. h                             // определение класса вектор,

// его данных и методов

#ifndef _VECTOR1_H                   //предотвращение многократного

#define _VECTOR1_H                  //включения заголовочного файла

#include <iostream>

using std::endl;

using std::cout;

class VECTOR1                  // Класс для работы с геометрическими векторами

{

       //Данные

       private:

              int   x, y; // Координаты конца вектора

//Методы

       public:

// Конструктор вызывается при создании нового объекта

// с инициализацией по умолчанию или значениями параметров

VECTOR1 (const int &x1=0, const int &y1=0): x(x1), y(y1){ }

       // можно и так:

       //VECTOR (const int &x1=0, const int &y1=0) { x = x1; y = y1; }

      

       // Конструктор копирования - вызывается при создании

// нового объекта с инициализацией другим объектом,

// при передаче параметра с типом класса по значению и

// при возврате значения объекта с типом класса из

// метода класса по return.

// Подставляемый метод

       VECTOR1 (const VECTOR1 &copy)//copy - источник копирования

{

x = copy.x; y=copy.y;

}

       // Присвоить значения координатам вектора: подставляемый метод.

       void Assign(const int &x1, const int &y1)

       {

                   x = x1; y = y1;

                   return;

       }

       // Перегрузка префиксного унарного оператора "++" с

       // помощью метода класса: прибавляет к вектору

       // единичный вектор и возвращает сам вектор (не ссылку!)

       // Метод не имеет явно передаваемых аргументов, так

       // как при вызове неявно получает адрес объекта (this),

       // для которого он вызван. Подставляемый метод

       VECTOR1 operator++(void)

       {

                   x++; y++;

                   return *this;        // Возвращается сам вектор после увеличения

       }

       // Перегрузка постфиксного унарного оператора "++" с

       // помощью метода класса: возвращает значение

       // вектора до прибавления единичного вектора. В

// методе "operator++(int)" параметр метода

       // игнорируется. Метод является подставляемым

       VECTOR1 operator++(int)

       {

                   VECTOR1 vec_tmp = *this;

                   x++; y++;

                   return vec_tmp;    // Возвращает значение вектора до увеличения

       }

       // Перегрузка бинарного оператора "+" с помощью метода

       // класса: суммирует два вектора и возвращает их

       // сумму (не ссылку!). Модификатор const гарантирует, что данный метод

// не изменит значений членов объекта, для которого он вызван.

// Определение будет дано далее.

       VECTOR1 operator+(const VECTOR1 &) const;

       // Перегрузка бинарной операции "=": подставляемый

// метод (при его наличии можно корректно

// использовать цепочки присваиваний).

// В примере этот метод можно было не записывать, так

// как в примере не используются цепочки присваиваний и

// нет полей указателей на области динамической

// памяти

       VECTOR1&  operator = (const VECTOR1 &v)    // Возвращает ссылку на объект

                                                                                         // this - левый операнд

                                                                                         // v - правый операнд

       {

                   this->x = v.x; this->y = v.y;

// А можно и так: x = v.x; y = v.y;

                   return *this;

       }

      

       // Печать значений координат вектора: объявление метода класса.

       // Определение будет дано далее.

       void print(void) const;

};

void VECTOR1::print(void) const

       {

                   cout<<"\n Coordinates of a vector: x= "

                              <<x<<" y= "<<y<<endl;

                   return;

       }

// Оператор "+" бинарный, поэтому метод "operator+"

// долж e н иметь помимо скрытого (this) еще один явно

// передаваемый параметр

VECTOR1 VECTOR1:: operator+(const VECTOR1  &vec) const

{

       // Создать вектор для получения суммы и в начале

// присвоить ему значение первого слагаемого

       VECTOR1   vec_tmp = *this;

       // Сложить векторы и возвратить результат

       vec_tmp.x += vec.x; vec_tmp.y += vec.y;

       return vec_tmp;  //сначала будет вызван конструктор копирования vec_tmp,

//а затем деструктор для удаления vec_tmp

}

#endif _VECTOR1_H

Файл class _ vector 1. cpp   // Тестирование класса VECTOR1

#include "vector1.h"

const int N = 5;            // Число векторов в массиве

Int main()

{

       //Создаем и инициализируем вектор v1 и массив векторов v_a,

//вызывается конструктор по умолчанию

       VECTOR1   v1, v_a[N];

                  

       //Задаем значения и печатаем массив векторов

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

       {

                   v_a[i].Assign(i, i+1);

                   cout<<"\n Vector v_a["<<i<<"]:";

                   v_a[i].print();

       }

      

       // К элементам массива векторов добавляем единичные вектора

       // и печатаем массив

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

       {

                   ++v_a[i];

                   cout<<"\n Vector v_a["<<i<<"]:";

                   v_a[i].print();

       }

       // Вычисляем сумму элементов массива векторов и печатаем ее

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

       {

                   v1 = v1 + v_a[i];

       }

       cout<<"\n Summa - vector v1: "; v1.print();

      

       cout<<endl;

       system("pause");

       return 0;

}

 

 

Результат работы программы:

 

 


Наследование и полиморфизм

 

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

На рисунке 4.1 приведена диаграмма классов Figure (фигура), Circle (окружность), Rectangle (прямоугольник).

Рисунок 4.1 – Диаграмма классов

Класс Figure – базовый класс, Circle и Rectangle – наследуемые классы.

 

Полиморфизм в С++ имеет две формы:

· перегрузка операций, функций, использование шаблонов. В этом случае определение конкретного экземпляра операции, функции или класса выполняется на этапе компиляции и называется статическим связыванием;

· использование виртуальных функций (основная форма полиморфизма). В этом случае определение конкретного экземпляра операции, функции или класса выполняется во время выполнения программы и называется динамическим связыванием.

 

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

 

Пример 4_3. Иерархия классов «Фигура», «Окружность», «Прямоугольник»

Файл fiures. h                // определение классов «Фигура», «Окружность»,

// «Прямоугольник», их данных и методов

# ifndef _ FIGURES _ H        //предотвращение многократного

#define _FIGURES_H       //включения заголовочного файла

#include <iostream>

using std::endl;

using std::cout;

class Figure                      // Базовый класс

{

// Методы

       public:

Figure(void)        // Конструктор по умолчанию

{

cout << "The constructor Figure" << endl;

}

virtual ~Figure(void) // Виртуальный деструктор

{

cout << "The destructor Figure" << endl;

}

      

// Нарисовать фигуру - чисто виртуальная функция

       // не имеет реализации в базовом классе

       virtual  void  draw(void) = 0;

};

class Circle: public Figure // Circle наследуется от Figure

{

// Данные

       private:

                   int   x, // x-координата центра

y, // y-координата центра

R; // Радиус окружности

// Методы

       public:

Circle(int CenterX, int CenterY, int Radius) // Конструктор

{

cout << "The constructor Circle" << endl;

x = CenterX; y = CenterY; r = Radius;

}

virtual ~Circle(void)                              // Виртуальный деструктор

{

cout << "The destructor Circle" << endl;

}

      

       // Нарисовать фигуру - виртуальная функция

       virtual void draw (void);

};

// Нарисовать окружность, реализация в классе Circle

void Circle::draw(void)

{

       cout << "The Circle draw" << endl;

}

class Rectangle: public Figure     // Rectangle наследуется от Figure

{

// Данные

       private:

                   int   l, // Левый верхний

T, // угол

R, // Правый нижний угол

b; // прямоугольника

// Методы

       public:

Rectangle(int L, int T, int R, int B) // Конструктор

{

                   cout << "The constructor Rectangle" << endl;

l = L; t = T; r = R; b = B;

}

      

virtual ~Rectangle(void)           // Виртуальный деструктор

{

cout << "The destructor Rectangle" << endl;

}

      

       // Нарисовать фигуру - виртуальная функция

       virtual void draw(void);

};

// Нарисовать прямоугольник, реализация в классе Rectangle

void Rectangle::draw(void)

{

       cout << "The Rectangle draw" << endl;

}

# endif _ FIGURES _ H

 

 

Файл der _ virt 1. cpp           // Тестирование классов «Фигура», «Окружность»,

// «Прямоугольник»

#include "figures.h"

int main()  // Тестирование классов Figure, Circle и Rectangle

{                           

// Создается массив указателей на базовый класс, который

// инициализируется адресами объектов классов Circle и Rectangle

Figure *figures[2];

figures[0] = new Circle(100, 100, 10);

figures [1] = new Rectangle (100, 100, 200, 250);

      

       // Для каждого объекта базового класса вызывается метод «рисования» фигуры

       figures[0]->draw();

       figures[1]->draw();

// Уничтожаются созданные объекты. Если бы деструкторы не были

// виртуальными, то при уничтожении объектов вызывался бы ~Figure() -

// деструктор базового класса, что приводило бы к ошибке

       delete figures[0]; delete figures[1];

      

       cout<<endl;

       system (" pause ");

       return 0;

}

 

Результат работы программы:

 

Обратите внимание на порядок вызова конструкторов – сначала базового, а потом наследуемого классса, и деструкторов – сначала наследуемого классса, а потом базового.

 

Использование виртуальных функций позволяет с помощью указателя на объект базового класса вызывать методы наследуемых классов (в примере draw) – это и есть полиморфизм!

 

Примеры программ

4.4.1 Класс «Одномерный динамический массив»

 

Данный класс содержит одномерный массив целых чисел, память для которых выделяется динамически.

Использование динамической памяти требует «глубинного» (по элементного) копирования в операциях присваивания с объектами данного класса. Поэтому в классе необходимо создать копирующий конструктор и перегрузить операцию присваивания. Для того, чтобы обеспечить повторное присваивание (например, a = b = c) перегружаемая функция должна возвращать ссылку!

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

Для проверки корректности выполнения программы в реализации методов класса используется макрос assert (выражение) из стандартной библиотеки assert. h. Если результат выражения, стоящего в скобках – ложь, то выполнение программы прерывается и выдается дианостическое сообщение.

 

 

Пример 4_4_1. Класс «Одномерный динамический массив»

Файл DinArray 1. h             //определение динамического массива и его операций

 

#ifndef _DIN_ARRAY1_Н //предотвращение многократного

#define _DIN_ARRAY1_Н //включения заголовочного файла

#include "assert.h"

#include <iostream>

using std::endl;

using std::cout;

class Array1D                   //динамический одномерный массив

{

       //Данные

       private:

       int *p;                //указатель на первый элемент массива целых чисел

                   int size;      //размерность массива

//Методы

       public:

       Array1D(int n=3); //конструктор по умолчанию(размерность массива = 3)

       Array1D(const Array1D&  a); //копирующий конструктор

       ~Array1D() {delete []  p;}   //деструктор освобождает память, выделенную

//для массива

       void print()const;             //вывод значений элементов массива

                   // перегружаемые операции:

       int& operator [](int i)const;                                // индексирование

       Array1D& operator =(const Array1D& a);         // присваивание

       Array1D operator +(const Array1D& a)const;  // сложение (бинарная операция)

};

//реализация методов класса Array 1 D

Array 1 D:: Array 1 D (int n): size (n) //конструктор по умолчанию

{

       assert (n >0);         //размерность массива должна быть положительной

       p = new int [ n ];       //выделяем память для элементов массива

       assert (p!=0);        //если память не выделена, то аварийное завершение

};

Array1D::Array1D(const Array1D& a): size(a.size)     // копирующий конструктор

{

       p = new int [ size ];   //выделяем память для элементов массива

       assert (p!=0);                    //если память не выделена, то аварийное завершение

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

                   p [ i ] = a. p [ i ]; //присваиваем значения элементам массива

}

void Array1D::print() const

{

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

                   cout<<p[i]<<endl;

       cout<<endl;

}

int & Array1D::operator [](int i) const

{

       assert (i >=0 && i < size); //если индекс за границами, то аварийное завершение

       return p [ i ];                   //возвращаем ссылку на i – й элемент массива

}

Array1D& Array1D::operator =(const Array1D& a)

{

       if (this!= &a){ //если присваивание самому себе, то ничего делать не надо

                   assert (size == a. size); //если размеры массивов не равны, то аварийное

// завершение

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

                   p[i] = a.p[i];

       }

       return *this;                      // возвращаем ссылку на объект Array1D

}

Array1D Array1D::operator +(const Array1D& a) const

{

       assert (size == a. size);       //если размеры массивов не равны, то аварийное

//завершение

       Array 1 D sum (size);        //создаем массив sum размерности size

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

                   sum.p[i] = p[i] + a.p[i]; // или sum.p[i]=this->p[i] +a.p[i];

       return sum;                       //сначала вызывается конструктор копирования,

//потом деструктор массива sum e

}

# endif _ DIN _ ARRAY 1_Н

 

Файл DinArray 1. cpp                    // Тестирование класса Array1D

 

#include "DinArray1.h"

Int main()

{

       Array1D a, b, c;                //Размерность массивов по умолчанию = 3

      

for (int i=0; i<3; ++i) a[i]=i+1;           // Инициализируем массив а

       cout<<"Array1D a: "<<endl;             //и выводим его значения

A.print();

      

b = a;                                                        //Присваиваем элементам массива b

//значения элементов массива  a

       cout<<"Array1D b: "<<endl;                 //и выводим значения массива b

B.print();   

a = a + b + (c = a + b);                          //Сначала определяем значения с,

//затем увеличиваем значения а

       cout<<"Array1D c: "<<endl;             //Выводим значения массива с

       c.print();

cout <<" Array 1 D a: " << endl;             //Выводим значения массива  а

A.print();

       system("pause");

       return 0;

}

 

 


Результат работы программы:

 

 

4.4.2 Класс «Динамически размещаемая срока»

 

Данный класс содержит строку произвольной длины, символы которой размещаются в памяти динамически.

В реализации методов класса используются функции из стандартной библиотеки < cstring >:

· strcpy _ s (s, len, str) – копирование не более len символов из строки str в строку s

· strcat _ s (s, len, str)  – добавление не более len символов из строки str в строку s

· strlen (str)           – длина строки str. Функция возвращает значение типа size _ t

Функции strcpy _ s, strcat _ s безопасные (контролируют число пересылаеых символов) по сравнению с функциями strcpy, strcat, прииспользовании котрыхкомпилятор выдает предупреждение:

«This function or variable may be unsafe. Consider using strcpy_s instead.»

 

Для проверки корректности выполнения программы в реализации методов класса используется макрос assert (выражение) из стандартной библиотеки assert. h.

 

Пример 4_4_2. Класс «Динамически размешаемая строка»

Файл DinString. h               //определение динамической строки и операций

#ifndef _DIN_STRING_Н //предотвращение многократного

#define _DIN_STRING_Н //включения заголовочного файла

#include <iostream>

using std::endl;

using std::cout;

#include <cstring>

#include "assert.h"

class DinString                  // динамически размещаемая срока

{

// Данные

       private:

       char *s;             //указатель на строку

       int len; //длина строки

//Методы

       public:

       DinString(): len(0){ // конструктор по умолчанию

                   s=new char[1]; assert(s!=0); s[0]=0;

       }

       DinString(const DinString& str); // копирующий конструктор

       DinString(const char* str);      // преобразующий конструктор -

                                                                  // преобразует тип char* к типу DinString

       ~DinString() {delete [] s;}         // деструктор освобождает память,

                                                                  // выделенную для строки

       void print()const { cout<<s<<endl;}// вывод строки

// перегружаемые операции

       DinString& operator =(const DinString& str);      // присваивание

       DinString operator + (const DinString& str) const; // конкатенация

};

// реализация методов класса DinString

DinString::DinString(const char* str) // преобразующий конструктор

{

       len = static_cast<int>(strlen(str));// преобразуем возвращаемое значение

// функции strlen типа size _ t в

// значение типа int

       s=new char[len+1];        //выделяем память для строки

       assert(s!=0);                    //если память не выделена, то аварийное завершение

       strcpy_s(s, len+1, str);

};

DinString::DinString(const DinString& str): len(str.len) // копирующий конструктор

{

       s=new char[len+1]; //выделяем память для строки

       assert(s!=0);        //если память не выделена, то аварийное завершение

       strcpy_s(s, len+1, str.s);

}

DinString& DinString::operator =(const DinString& str)

{

       if (this!= &str){ //если присваивание самому себе, то ничего делать не надо

                   delete [] s;                        //удаляем "старую" строку

                   len = str.len;                     //формируем длину новой строки

                   s=new char[len+1];        //выделяем память для строки

                   assert(s!=0);        //если память не выделена, то аварийное завершение

                   strcpy_s(s, len+1, str.s);

       }

       return *this;                             //возвращаем ссылку на объект

}

DinString DinString::operator + (const DinString& str) const

{

       DinString tmp;

       tmp.s =new char[len + str.len +1];

       tmp.len=len + str.len;

       strcpy_s(tmp.s, tmp.len+1, s);

       strcat_s(tmp.s, tmp.len+1, str.s);

       return tmp;                       //сначала вызывается конструктор копирования,

                                                      //потом деструктор tmp

}

#endif _DIN_STRING_Н

 

 

Файл DinSring. cpp                    // Тестирование класса DinString

 

#include "DinString.h"

Int main()

{

       DinString s1("123"), s2("456"), s3(s1), s4;

       s4=s3 + s2;

       cout<<"s1: "; s1.print(); cout<<"s2: "; s2.print();

cout<<"s3: "; s3.print(); cout<<"s4: "; s4.print();

       cout<<endl;

       system("pause");

       return 0;

}

 

 

Результат работы программы:

 

 





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


Дата добавления: 2018-10-14; Мы поможем в написании ваших работ!; просмотров: 348 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Наука — это организованные знания, мудрость — это организованная жизнь. © Иммануил Кант
==> читать все изречения...

2789 - | 2600 -


© 2015-2025 lektsii.org - Контакты - Последнее добавление

Ген: 0.011 с.