Наследование операторных функций

Практическая работа № 8

Тема: «Наследование и виртуальные функции»

Примеры решения задач

Далее приводятся примеры решения задач, в которых используется меха­низм наследования.

Комплексные числа

Листинг 3.1. Комплексные числа

#include <iostream>

#include <cmath>

using namespace std;

//Базовый класс:

class ComplAlg{

protected:

//Действительная и мнимая часть:

double х,у;

public:

//Заполнение полей класса:

void set(double х,double у){

this->x=x;

this->y=y;}

//Отображение комплексного числа:

virtual void show(){

cout<<"alg: z=";

if(y==0){

cout<<x<<endl;

return;}

if(x!=0) cout<<x;

if (x!=0&&y>0) cout«,, + ";

if(у!=l&&y!=-l) cout<<y;

if (y==-l) cout«"-"; cout<<"i\n";}

//Конструктор (без аргументов):

ComplAlg(){

x=0; y=0; }

//Конструктор (с аргументами):

ComplAlg(double x,double y){

this->x=x;

this->y=y;}

} z 1, z 2 (2,-1) ;

//Производный класс:

class Compl:public ComplAlg{

protected:

//Модуль и аргумент:

double r,phi;

public:

//Заполнение полей класса (переопределение метода):

void set(double x,double y){

ComplAlg: :set(x, у) ;

r=sqrt(x*x+y*y) ;

phi=atan2(y,x) ; }

//Отображение числа (переопределение метода):

void show(){

ComplAlg: :show () ;

cout<<"trig: z=";

if (phi==0) {

cout«r«"\n" ;

return;}

if(r!=l) cout<<r;

cout«"exp (";

if (phi ! =l&&phi ! =-l) cout«phi;

if (phi==-l) cout«"-";

cout«"i) \n"; }

//Конструктор без аргументов:

Compl():ComplAlg(){

r=0;

phi=0;}

//Конструктор с аргументами:

Compl(double x,double y) :ComplAlg(x, y) {

r=sqrt(x*x+y*y);

phi=atan2(y,x);}

} z3,z4(cos(1),-sin(1));

int main(){

zl.show();

zl.set(3,4) ;

zl.show ();

z2.show();

z3.set(-1,0);

z3.show ();

z4.show();

return 0; }

В базовом классе ComplAlg объявлены защищенные поля х и у (действи­тельная и мнимая части). Для работы с этими полями описываются вирту­альные методы set () и show (). У метода set () два аргумента, которые определяют действительную и мнимую части комплексного числа. Метод show () используется для отображения комплексного числа. Некоторая за­мысловатость кода метода объясняется теми правилами, которые применя­лись для отображения комплексного числа в алгебраической форме. Кратко они сводятся к тому, чтобы форма представления числа внешне совпадала со стандартной математической формой отображения комплексных чисел. Основные положения, реализованные в методе show (), следующие:

a. Если число действительное (мнимая часть равна нулю), нулевая мнимая часть не отображается.

b. Нулевая действительная часть не отображается.

c. Если мнимая часть по модулю равна единице, эта единица не ото­бражается (но отображается знак - плюс или минус).

Для того чтобы легче было различать, о какой форме представления ком­плексного числа идет речь, в начале строки вывода отображается метка alg.

В классе описаны два конструктора: без аргументов и с двумя аргумента­ми. Конструктором без аргументов создается нулевое комплексное число, а конструктор с двумя аргументами позволяет при создании задать дей­ствительную и мнимую части.

Производный класс Compl создается на основе базового класса ComplAlg. Помимо наследуемых полей, в классе добавляется еще два защищенных поля: модуль и аргумент комплексного числа. В этом классе также перео­пределяются методы set () и show (). Обращаем внимание, что в каждом из этих методов вызывается старая версия метода из базового класса: в этом случае перед именем метода, через оператор расширения контекста, ука­зывается имя базового класса. Метод set () расширяется так, чтобы вме­сте с изменением полей х и у изменялись соответствующим образом поля г и phi. Метод show{) переопределяется так, что кроме алгебраической формы числа отображается и его тригонометрическая форма (перед триго­нометрической формой указывается метка trig). При отображении триго­нометрической формы числа также применяются некоторые правила:

d. Единичный модуль не отображается.

e. В случае нулевого аргумента комплексная экспонента не отобра­жается.

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

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

В главном метоле программы в основном выполняются команды по измене­нию значений полей объектов и отображению значений на экран. В резуль­тате выполнения программы получаем:

alg: z=0

alg: z=3+4i

alg: z=2-i

alg: z=-l

trig: z=exp(3.14159i)

alg: z=0.540302-0.841471i

trig: z=exp(-i)

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

Наследование операторных функций

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

Листинг 3.2. Наследование операторных функций

#include <iostream>

using namespace std;

//Базовый класс:

class Compll{

public:

//Действительная и мнимая часть:

double Re,Im;

//Перегрузка оператора сложения:

Compll operator+(Compll obj){

Comp11 tmp;

tmp.Re=Re+obj.Re;

tmp. Im=Im+ob j . Inu­re turn tmp; }

//Конструктор с двумя аргументами:

Compll(double x,double y){

Re=x ;

Im=y;}

//Конструктор без аргументов:

Compll(){

Re = 0 ;

lm=0;}

}al(1,2),a2,a3(3,4);

//Производный класс:

class Compl2:public Compll{

public:

//Перегрузка оператора умножения:

Compl2 operator*(Compl2 obj){

Comp12 tmp;

tmp.Re=Re*obj.Re-Im*obj.Im;

tmp.Im=Re*obj.Im+Im*obj.Re;

return tmp;}

//Конструктор с аргументами:

Compl2(double x,double у):Compll(x,y){}

//Конструктор без аргументов:

Compl2():Compll(){}

}bl(10,20),b2(30,40),b3;

//Внешняя операторная функция:

Compl2 operator-(Compll х,Сотр11 у){

Сотр12 tmp;

tmp.Re=x.Re-y.Re;

tmp.Im=x.Im-y.Im;

return tmp; }

//Внешний метод для отображения полей объекта:

void show(Compll obj){

cout«”Re: "«ob j . Re<<endl;

cout«"Im: "<<obj . Im«endl; }

int main(){

//Сумма объектов:

a2=al+bl;

show(a2);

a2=bl+al;

show(a2);

a2=bl+b2;

show(a2);

//Произведение объектов:

b3=bl*b2;

show(b3);

//Разность объектов:

a2=al-аЗ;

show(a2);

b3=bl-b2;

show(b3);

return 0;}

В базовом классе Compll поля Re и Im (действительная и комплексная части) объявлены как открытые. Класс имеет конструктор без аргументов и конструк­тор с двумя аргументами. Однако главное внимание в данном случае следует уделить перегруженному оператору сложения: при сложении двух объектов класса Compll получаем объект того же класса, а его поля равны сумме со­ответствующих полей исходных объектов. В этом смысле переопределение оператора достаточно стандартное. Интересно будет проследить возможности использования оператора сложения с учетом наличия производного класса.

В производном классе Сотр12 определяются конструкторы (без аргумен­тов и с двумя аргументами, причем все действие этих конструкторов сво­дится к вызову соответствующего конструктора базового класса). Кроме конструкторов, в классе перегружается оператор умножения.

Представляет интерес также внешняя операторная функция, переопреде­ляющая оператор вычитания. Сразу обращаем внимание читателя, что ар­гументами функции являются объекты класса Compll, а результат - объект класса Comp 12. Сделано это для того, чтобы с помощью данной функцииможно было вычислять разность не только объектов класса Compll, но и объектов класса Сошр12.

Внешняя функция show () используется для отображения значения полей объекта, указанного аргументом функции. В прототипе функции указано, что аргументом является объект класса Compll.

В главном методе программы проверяется работа перегруженных операто­ров при работе с объектами базового и производного классов. В результате выполнения программы получаем:

Re: 11

Im: 22

Re: 11

Im: 22

Re: 40

Im: 60

Re: -500

Im: 1000

Re: -2

Im: -2

Re: -20

Im: -20

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

Во-первых, операторные функции наследуются. Во-вторых, объект произ­водного класса может присваиваться в качестве значения объекту базового класса. Этими двумя обстоятельствами объясняется возможность выпол­нять или не выполнять те или иные операции. Так, например, могут склады­ваться объекты класса Compll и Сотр12. Результат присваивается объекту класса Compll (команда a2=al+bl в главном методе программы). Напом­ним, что операторная функция для оператора сложения описана в классе Compll. При вычислении результата выражения al+Ы операторная функ­ция вызывается из объекта а 1, а ее формальным аргументом является объ­ект Ы класса Сотр12. В прототипе операторной функции аргумент отно­сится к классу Compll. Однако поскольку объекты производного класса могут присваиваться объектам базового класса, аргументом операторной функции может быть и объект производного класса Comp 12.

Несколько иная ситуация имеет место при выполнении команды a2=bl+al, в которой к объекту класса Сотр12 прибавляется объект класса Compll. В этом случае операторная функция для оператора сложения вызывает­ся из объекта Ы класса Сотр12, что становится возможным благодаря наследованию соответствующей операторной функции. В силу тех же при-

чин легитимна и команда а2=Ы+Ь2: операторный метод вызывается из объекта класса Сотр12 благодаря наследованию, а второй операнд являет­ся объектом класса Comp 12 на правах наследника базового класса.

Этого никак нельзя сказать о произведении объектов: соответствующая операторная функция объявлена в производном классе Сошр12, вызывает­ся из объекта этого класса, ее аргументом является объект класса Сотр12, результатом является объект того же класса. Команда ЬЗ=Ы *Ь2 иллюстри­рует ситуацию.

Команды а2=а1-аЗ и ЬЗ=Ы-Ь2 иллюстрируют возможности внешней опе­раторной функции для оператора вычитания. В качестве результата функ­цией возвращается объект класса Сотр12, поэтому результат вычисления разности двух объектов можно записать не только в объект класса Сотр12, но и в объект базового класса Compll. Аргументами функции являются объекты класса Compll, поэтому в качестве аргументов можно передавать и объекты производного класса Comp 12 (оба аргумента или один из них). По той же причине аргументом функции show () могут передаваться объ­екты обоих классов.

Наши рекомендации