Объектно-ориентированное программирование - Учебное пособие (А.А. Хусаинов)

1.5. перегрузка функций и операций

 

В Си++ различные функции могут иметь одинаковое имя. Такие функции называются перегружаемыми. Цель перегрузки (присвоения одинаковых имен) функций состоит в том, чтобы функция выполнялась по-разному в зависимости от типа и количества ее аргументов. Например, функция вычисления модуля целого числа, числа с плавающей точкой и вектора будет выполняться по-разному:

 

#include <stdio.h>                    //библиотека стандартного ввода-вывода

#include <math.h>                    //библиотека математических функций

struct Vector3d {                      //структура трёхмерного вектора

               double x,y,z;   //состоит из трёх координат в пространстве

};

 

double absl(double x)    //эта функция возвращает модуль double

{

               if(x<0) return -x;//воспользуемся определением модуля

else return x;

}

 

double absl(Vector3d v)            //эта функция возвращает модуль(длину)

                                                   //трёхмерного вектора

{

               return sqrt(v.x*v.x+v.y*v.y+v.z*v.z); //корень квадратный из

                                                                           //суммы квадратов координат

}

int absl(int i)                  //эта функция возвращает модуль целого числа

{

               if(i<0) return -i;//воспользуемся определением модуля

               return i;

}

 

main()

{

  Vector3d n={3.14159, 2.71828, -1}; //n-трёхмерный вектор

  printf(" Входные данные: ");

  printf("Трёхмерный вектор n={\%f,\%f,\%f} ",n.x,n.y,n.z);

  printf(" Выходные данные:");

  printf(" Модуль   вектора  n  равен   \%f",absl(n));

//найдём модуль n

  printf(" Модуль целого числа -1 равен \%d",absl(-1));

//вызов функции для int

  printf(" Модуль double числа -1 равен \%f",absl(-1.));

//вызов функции для double

}

 

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

 

Входные данные:

Трёхмерный вектор n={3.141590,2.718280,-1.000000}

Выходные данные:

Модуль   вектора  n  равен   4.273012

Модуль целого числа -1 равен 1

Модуль double числа -1 равен 1.000000

 

Аналогичным образом перегружаются операции. Перегрузка операций осуществляется для типов данных, определяемых структурами. Операция перегружается как функция, имя которой состоит из слова operator с добавленным справа символом операции. Например, подпрограмму конкатенации строк, определяемых структурой

 

Struct String{

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

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

}

 

можно определить как операцию сложения строк

 

String operator+(String s,String t);

 

Приведём пример программы, в которой определена такая операция:

 

#include <stdio.h>                    //библиотека стандартного ввода-вывода

#include <string.h>                   //библиотека функций для работы со строками

#include <conio.h>                    //библиотека консольного ввода-вывода

struct string {     //структура string

               int length;        //содержит длину

               char *p;           //и саму строку

};

string operator+(string s, string t)                      //перегрузка операции +

{

   int i;

   string res;                                                                     //результирующая строка

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

strcpy(res.p, s.p);                                               //копируем первую строку

strcpy(res.p+s.length, t.p);                     //копируем вторую строку

res.length=s.length+t.length;                 //заполняем поле структуры- //длина строки

return res;

}

 

void main()

{

  string s1={3,"abc"}, s2={4,"1234"},s3;    //строки s1,s2,s3

  clrscr();

  printf("Входные данные: ");

  printf(" Первая строка          \%s ",s1.p);

  printf("Длина первой строки    \%d ",s1.length);

  printf("Вторая строка          \%s ",s2.p);

  printf("Длина второй строки    \%d ",s2.length);

  s3=s1+s2;                     //используем перегруженную

                                       //операцию +

  printf(" Выходные данные: ");

  printf("Результат конкатенации первой и второй строк  \%s ",s3.p);

      printf("Длина результирующей строки                 \%d ",s3.length);

                                                     //результат конкатенации s1

                                                     //s2 - "abc1234" длина - 7

}

 

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

 

Входные данные:

Первая строка                          abc

Длина первой строки                          3

Вторая строка                          1234

Длина второй строки                          4

 

Выходные данные:

Результат конкатенации первой и второй строк     abc1234

Длина результирующей строки                                             7

 

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

 

#include <stdio.h>                    //библиотека стандартного ввода-вывода

#include <string.h>                   //библиотека функций для работы со строками

#include <conio.h>                    //библиотека консольного ввода-вывода

struct string {     //структура string

               int length;        //содержит длину

               char *p;           //и саму строку

};

string operator+(string s, string t)                      //перегрузка операции +

{

   int i;

   string res;                                                                     //результирующая строка

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

for (i=0; i<s.length; i++)

               res.p[i]=s.p[i];                          //копируем первую строку

for (i=s.length; i<s.length+t.length; i++)

               res.p[i]=t.p[i-s.length];            //копируем вторую строку

res.p[i]=0;                                                          //строка завершается 0

res.length=s.length+t.length;                 //заполняем поле структуры- //длина строки

return res;

}

 

void main()

{

  string s1={3,"abc"}, s2={4,"1234"},s3;    //строки s1,s2,s3

  clrscr();

  printf("Входные данные:",s3.p);

  printf(" Первая строка          \%s ",s1.p);

  printf("Длина первой строки    \%d ",s1.length);

  printf("Вторая строка          \%s ",s2.p);

  printf("Длина второй строки    \%d ",s2.length);

  s3=s1+s2;                     //используем перегруженную операцию +

  printf(" Выходные данные: ");

  printf("Результат конкатенации первой и второй строк  \%s ",s3.p);

  printf("Длина результирующей строки                \%d ",s3.length);

                                                     //результат конкатенации s1

                                                     //s2 - "abc1234" длина - 7

}

 

Отметим, что невозможно определить эту операцию с помощью

char* operator+(char* s, char* t)           ,

поэтому приходится объявлять структуру, содержащую строку.

 

Правила составления перегружаемых функций и операций:

для перегружаемых операций (над структурами) отсутствует возможность передачи параметров по умолчанию;

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

int f(int x=0);

int f();

ибо неясно, к вызову какой из этих функций приводит оператор y=f();

перегружаемые функции и операции не могут различаться только по типу возвращаемого значения, например, объявление функций:

Void f(int);

int f(int);

является ошибочным.

 

Пример. Рассмотрим структуру, реализующую двумерный вектор. Определим для него операции суммы, разности, унарного минуса, скалярного произведения.

 

#include <stdio.h>                    //библиотека стандартного ввода-вывода

 

struct Vector {   //структура вектора на плоскости

   double x,y;                  //состоит из координат х и у

};

 

Vector operator+(Vector v, Vector w) //перегрузим операцию сложения

{

   Vector t;

t.x=v.x+w.x; t.y=v.y+w.y; //складываются соответствующие координаты

                                                    //двух векторов

   return t;

}

 

Vector operator-(Vector v, Vector w) //перегрузим операцию вычитания

{

   Vector t;

   t.x=v.x-w.x; t.y=v.y-w.y;        //находится разность соответствующих

                                                               //координат двух векторов

   return t;

}

 

Vector operator-(Vector v) //перегрузим операцию унарного минуса

{

   Vector t;

   t.x=-v.x; t.y=-v.y;          //найдём вектор,противоположно направленный

                                                     //и имеющий ту же длину, для данного

   return t;

}

double operator*(Vector v, Vector w) //перегрузим операцию умножения

{

   return v.x*w.x+v.y*w.y;//найдём скалярное произведение двух векторов

}

 

int main()

{

  Vector a={1,0}, b={-1,1},c,d,e;

  printf (" Входные данные: ");

  printf ("Вектор а={\%f,\%f},b={\%f,\%f} ",a.x,a.y,b.x,b.y);

  c=a-b;

  printf(" Результат вычитания a-b={\%f,\%f}",c.x,c.y);        //вычитание

 printf(" Результат скалярного произведения a*b=\%f",a*b);  

//произведение

  d=a+b;

  printf(" Результат сложения  a+b={\%f,\%f}",d.x,d.y);       //сложение

  e=-a ;

  printf(" Вектор противоположный а это вектор е={\%f,\%f}",e.x,e.y);

//унарный минус

}

 

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

 

Входные данные:

Вектор а={1.000000,0.000000},b={-1.000000,1.000000}

 

Выходные данные:

Результат вычитания a-b={2.000000,-1.000000}

Результат скалярного произведения a*b=-1.000000

Результат сложения  a+b={0.000000,1.000000}

Вектор противоположный а это вектор е={-1.000000,-0.000000}