Перегрузка функций.


Лекция 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;

}

//---------------------------------------------------------------------------