Наследование операторных функций
Практическая работа № 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 . Inure 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 () могут передаваться объекты обоих классов.