Перегрузка функций.
Лекция 14
Тема: Создание классов и их потомков.
Рассмотрим пример создания класса Och (очередь).
#include <iostream.h>
#include <conio.h>
class Och
{
int q[10];
int beg, end; // - указывают на начало и конец очереди
public:
Ocht(void);
void put(int); // - положить элемент в очередь
int get(void); // - взять элемент из очереди
};
Och::Och(void)
{
beg=end=0;
}
int Och::get(void)
{ if(beg==end)
{ cout<<"Pusto";
return 0;
}
return q[beg++];
}
void Och::put(int i)
{
if(end==10)
{ cout<<"Polno";
return;}
q[end++]=i; }
int main(int argc, char* argv[])
{
Och a,b;
a.put(7);
a.put(8);
a.put(9);
cout<<a.get()<<" ";
cout<<a.get()<<" ";
cout<<a.get()<<" ";
cout<<a.get()<<"\n\n";
for (int i=0;i<10;i++) // заполнение очереди числами i*i
b.put(i*i);
for(int i=0;i<10;i++)
cout<<b.get()<<" ";
cout<<"\n\n";
getch();
return 0;
}
Механизм наследования позволяет определять новые классы на основе уже имеющихся. Класс, на основе которого создается новый, называют базовым (родительским), а новый класс – производным (наследником). Любой производный класс может в свою очередь стать базовым классом для других создаваемых классов. Так формируется иерархия объектов. В иерархии объектов производный объект имеет возможность доступа к элементам данных и методам объектов базового класса.
В языке существует возможность одиночного и множественного наследования. При одиночном наследовании базовым является один класс, при множественном наследовании базовыми классами должны быть несколько классов.
Основная форма наследования:
class имя_наследующего_класса:режим доступа наследуемый_класс{…};
режим доступа – это одно из ключевых слов private, public,protected.
Для класса Och добавим класс Och1, который позволяет осуществлять вычисление суммы целых чисел в очереди с помощью функции get_sum().
#include <iostream.h>
#include <conio.h>
class Och
{ protected: // используется в базовых классах при наследовании.
int q[10];
int beg, end;
public:
void init(void);
void put(int);
int get(void);
};
class Och1: public Och
{ int sum;
public:
int get_sum(void);
void show_sum(void);
};
void Och::init(void)
{beg=end=0;
}
int Och::get(void)
{ if(beg==end)
{ cout<<"Pusto";
return 0;
}
return q[beg++];
}
void Och::put(int i)
{
if(end==10)
{ cout<<"Polno";
return;}
q[end++]=i;
}
int Och1::get_sum(void)
{
sum=0;
for(int i=beg;i<end;i++)
sum+=q[i];
return(sum);
}
void Och1::show_sum(void)
{
cout<<"summa - "<<sum<<"\n\n";
}
int main(int argc, char* argv[])
{
Och1 a;
a.init();
for (int i=0;i<4;i++) // заполнение очереди числами i*i
a.put(i*i);
a.get_sum();
a.show_sum();
getch();
return 0;
}
В классе Och режим доступа protected. Значит все функции-члены класса Och1 имеют доступ к членам класса Och так, как если бы были объявлены внутри класса Och1. Однако функции класса Och1 не имеют доступа к приватной части класса Och.
Полиморфизм – свойство, позволяющее использовать одно имя для обозначения действий, общих для родственных классов. Важнейшие формы полиморфизма в С++:
- Перегрузка функций и операций;
- Виртуальные функции;
- Общие функции или шаблоны.
Перегрузка функций и операций – это статический полиморфизм, так как он поддерживается на этапе компиляции.
Виртуальные функции относятся к динамическому полиморфизму, так как он реализуется при выполнении программы.
В С++ допускается наличие нескольких одноименных функций, выполняющих аналогичные действия над данными разных типов. Например, пусть в программе определены две функции с прототипами:
int max(int,int);
float max(float,float);
В программе можно указать следующие операторы:
float x,y;
cout<<”max=”<<max(5,8)<<endl;
x=12.5; y=25.12;
cout<<”max=”<<max(x,y);
В процессе компиляции программы при обращении к функции max() в зависимости от типа и числа аргументов будет осуществляться загрузка нужного экземпляра функции. Этот механизм называется перегрузкой функций.
Если в программе описано несколько одноименных функций, то при компиляции возможны следующие ситуации:
1. если возвращаемые значения, тип и число параметров нескольких функций совпадают, то второе и последующие объявления переопределяют первое.
2. если тип и число параметров нескольких одноименных функций совпадают, но возвращаемые значения различны, то второе и последующие объявления при компиляции рассматриваются как ошибочные (будет сообщение об ошибке от компилятора).
3. если тип и число параметров нескольких одноименных функций различны (например имеют различия по типу и количеству), то функция считается перегружаемой при условии объявления ее экземпляров в одной области видимости. Например,
#include <iostream.h>
int max(int,int);
float max(float,float);
void main()
{
….
}
float max(float c, float d)
{
if (c>d) return ( c );
return ( d );
}
int max(int a, int b)
{
if (a>b) return ( a );
return ( b );
}
Здесь два экземпляра перегружаемой функции max.
Другим способом реализации полиморфизма является перегрузка операций. Например, операции << и >> в С++ имеют двоякий смысл: поразрядный сдвиг и оператор ввода/вывода на консоль. Причина этого в том, что в заголовочном файле iostream.h эти знаки операций перегружены. Когда перегружается знак операции, компилятор анализирует тип операндов и в зависимости от типа делает выбор.
Переопределение (перегрузка) может выполняться почти для всех стандартных операций, кроме:. , .* , ?: , :: , sizeof.
Переопределение операции выполняется с помощью определения операторной функции следующего формата:
тип_результата operator знак_операции (список параметров){операторы тела операторной функции}
тип_результата – тип возвращаемого значения при выполнении переопределенной операции;
список параметров – список типов параметров при наличии которых у аргументов обращения к операции с данным знаком будет производиться перегрузка (вызов) именно этого экземпляра операции.
Операторная функция может быть компонентной функцией класса:
тип_результата класс :: operator знак_операции (список параметров)
{операторы тела операторной функции};
Если необходимо, то можно указать прототип операторной функции:
тип_результата operator знак_операции (список параметров)
Пример. Перегрузка операций == , < , > для класса комплексных чисел.
#include <iostream.h>
#include <math.h>
#include <conio.h>
#define FALSE 0
#define TRUE 1
class Complex{
private:
double real;
double image;
public:
Complex(double r){ real=r; image=0;} // конструктор для одного параметра
Complex(double r,double i){real=r;image=i;} //конструктор для 2-х параметров
~Complex(void){}
int operator==(Complex&); // прототип операторной функции ==
int operator>(Complex&); // прототип операторной функции >
int operator<(Complex&); // прототип операторной функции <
};
int Complex::operator==(Complex&C)
{return((sqrt(real/real+image*image)==sqrt(C.real*C.real+C.image*C.image))?TRUE:FALSE);
}
int Complex::operator>(Complex&C)
{return((sqrt(real/real+image*image)>sqrt(C.real*C.real+C.image*C.image))?TRUE:FALSE);
}
int Complex::operator<(Complex&C)
{return((sqrt(real/real+image*image)<sqrt(C.real*C.real+C.image*C.image))?TRUE:FALSE);
}
#pragma argsused
int main(int argc, char* argv[])
{
Complex c1(5.2,10.2),c2(5.2,10.1);
if(c1==c2)cout<<"c1 equal c2"<<endl;
if(c1<c2)cout<<"c1<c2"<<endl;
if(c1>c2)cout<<"c1>c2"<<endl;
getch();
return 0;
}
//---------------------------------------------------------------------------