Главная->Інформатика та програмування->Содержание->Пример с английскими мерами длины

ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 11) онлайн

Пример с английскими мерами длины

Иногда дружественные функции слишком удобны, чтобы от них отказываться.

Наверное, самым общим примером является ситуация, когда они используются

для повышения многосторонности перегружаемых операций. Следующая про-

грамма показывает ограниченность применения таких операторов без использо-

вания дружественных функций. Пример является вариацией на тему ENGLPLUS

и ENGLCONV из главы 8 «Перегрузка операций». Программа называется NOFRI.

Листинг 11.10. Программа NOFRI

 

// nofri.cpp

// ограничение перегрузки оператора «+»

#include <iostream>

using namespace std;

///////////////////////////////////////////////////////////

class Distance             //Класс английских расстояний

    {

    private:

      int feet;

      float inches;

    public:

      Distance() : feet(0), inches(0.0) //конструктор

                                        // (без аргументов)

      {  }                              //конструктор (1 арг.)

      Distance(float fltfeet)           //переводит float в

                                        //Distance

      {                                 //feet — это целая часть

      feet = static_cast<int>(fltfeet);

        inches = 12*(fltfeet-feet);     //слева — дюймы

      }

    Distance(int ft, float in)         //конструктор (2 арг.)

      { feet = ft; inches = in; }

    void showdist()                    //вывод на экран расстояния

      { cout << feet << "\'-" << inches << '\"'; }

    Distance operator + (Distance);

    };

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

                    //прибавить расстояние к d2

Distance Distance::operator + (Distance d2) //сумма

    {

    int f = feet + d2.feet;       //добавить футы

    float i = inches + d2.inches; //добавить дюймы

    if(i >= 12.0)                 //если сумма превышает 12.0,

      { i -= 12.0; f++;  }        //уменьшить на 12 дюймов,

                                  //прибавить 1 фут

 

 

Листинг 11.10 (продолжение)

 

return Distance(f,i);         //Новый Distance с суммой

    }

///////////////////////////////////////////////////////////

int main()

{

    Distance d1 = 2.5;           //конструктор переводит

    Distance d2 = 1.25;          //feet типа float в Distance

    Distance d3;

    cout << "\nd1 = "; d1.showdist();

    cout << "\nd2 = "; d2.showdist();

 

    d3 = d1 + 10.0;              //distance + float: OK

    cout << "\nd3 = "; d3.showdist();

// d3 = 10.0 + d1;              //float + Distance: ОШИБКА

// cout << "\nd3 = "; d3.showdist();

    cout << endl;

    return 0;

}

 

В этой программе оператор «+» переопределен на сложение двух объектов

типа Distance. К тому же есть конструктор с одним аргументом, который перево-

дит значение типа float, представляющее футы и десятые части футов, в значе-

ние типа Distance (то есть переводит числа из формата 10.25' в 10'-З").

Когда есть такой конструктор, можно написать такое выражение в main();

d3 = d1 + 10.0;

Перегружаемому + нужны объекты типа Distance и справа, и слева, но, найдя

справа аргумент типа float, в соответствии с конструктором он переводит этот

float в distance и осуществляет сложение.

Но вот что происходит при едва уловимом изменении в выражении:

d3 = 10.0 + d1;

Разве это выражение обрабатывается как следует? К сожалению, нет, так как

объект, чьим методом является перегружаемый +, должен быть переменной, на-

ходящейся слева от оператора. Когда мы туда помещаем оператор другого типа

или константу, компилятор использует + для сложения с этим типом (float в

данном случае) вместо того, чтобы добавлять объекты типа Distance. Увы, этот

оператор не умеет конвертировать float в Distance, поэтому не может выполнить

сложение. Вот результат работы NOFRI:

d1 = 2'-6''

d2 = 1'-З''

d3 - 12'-6''

Второе сложение не будет скомпилировано, поэтому эти выражения закоммен-

тированы. Выйти из этой ситуации можно, создав новый объект типа Distance:

d3 = Distance(10. 0) + d1;

Но такое выражение ни очевидно, ни элегантно. Как написать нормальное,

красивое выражение, которое могло бы содержать нужные типы как справа, так

и слева от оператора? Как вы уже, наверное, догадались, нам поможет дружест-

венная функция. Программа FRENGL демонстрирует, как именно.

 

Листинг 11.11. Программа FRENGL

 

// frengl.cpp

// Дружественная перегружаемая операция +

#include <iostream>

using namespace std;

///////////////////////////////////////////////////////////

class Distance                //Класс английских расстояний

  {

  private:

    int feet;

    float inches;

  public:

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

      { feet = 0; inches = 0.0; }

    Distance( float fltfeet ) //конструктор (1 арг.)

      {    //Переводит float в Distance

      feet = int(fltfeet);    //feet – целая часть

      inches = 12*(fltfeet-feet); //слева - дюймы

      }

    Distance(int ft, float in)    //конструктор (2 арг.)

      { feet = ft; inches = in; }

    void showdist()              //Вывести длину

      { cout << feet << "\'-" << inches << '\"';}

    friend Distance operator + (Distance, Distance); //дружественный

  };

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

 

Distance operator + (Distance d1, Distance d2) // d1 + d2

  {

  int f = d1.feet + d2.feet;       //+ футы

  float i = d1.inches + d2.inches; //+ дюймы

  if(i >= 12.0)                    //если больше 12 дюймов,

    { i -= 12.0; f++;  }           //уменьшить на 12 дюймов,

                                   //прибавить 1 фут

  return Distance(f,i);            //Новая длина с суммой

  }

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

int main()

{

  Distance d1 = 2.5;         //конструктор переводит

  Distance d2 = 1.25;        //float-feet в Distance

  Distance d3;

  cout << "\nd1 = "; d1.showdist();

  cout << "\nd2 = "; d2.showdist();

 

  d3 = d1 + 10.0;            //distance + float: OK

  cout << "\nd3 = "; d3.showdist();

  d3 = 10.0 + d1;            //float + Distance: OK

  cout << "\nd3 = "; d3.showdist();

  cout << endl;

  return 0;

}

 

Итак, перегружаемая операция + сделана дружественной:

friend Distance operator + (Distance, distance);

 

Обратите внимание, что для перегружаемого + первый аргумент — метод, то-

гда как второй — дружественная функция. Будучи обычным методом, + опери-

ровал одним из объектов как объектом, членом которого он был, а вторым —

как аргументом. Став дружественным, + стал рассматривать оба объекта в каче-

стве аргументов.

Единственное изменение, которое мы сделали, заключается в том, что пере-

менные feet и inches, использовавшиеся в NOFRI для прямого доступа данных

объекта, были заменены в FRENGL переменными d1.feet и d1.inches, так как этот

объект теперь стал аргументом.

Помните, что для того, чтобы сделать функцию дружественной, только объ-

явление функции внутри класса нужно начинать с ключевого слова friend.

Определение класса пишется без изменений, как и вызов функции.

 

15