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

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

Объекты в качестве аргументов функций

В следующей нашей программе мы внесем улучшения в пример ENGLOBJ, а также продемонстрируем несколько новых аспектов создания классов: перегрузку кон- структоров, определение методов класса вне класса и, возможно, самое важное —

использование объектов в качестве аргументов функций. Рассмотрим программу ENGLCON:

// englcon.cpp

// constructors,adds objects using member function

#include <iostream>

using namespace std;

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

class Distance               // длина в английской системе

{

  private:

    int feet;

    float inches;

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

    Distance() : feet(0),inches(0.0)

      { }

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

    Distance(int ft,float in) : feet(ft),inches(in)

      { }

    void getdist()           // ввод длины пользователем

    {

      cout << "\nВведите число футов: "; cin >> feet;

      cout << "Введите число дюймов: "; cin >> inches;

    }

    void showdist()          // вывод длины на экран

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

    void add_dist( Distance, Distance );            // прототип

};

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

// сложение длин d1 и d2

void Distance::add_dist(Distance dd1, Distance dd2)

{

  inches = dd1.inches + dd2.inches;      // сложение дюймов

  feet = 0;                              // с возможным заемом

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

  {                                      // то уменьшаем число дюймов

    inches -= 12.0;                      // на 12.0 и увеличиваем

    feet++;                              // число футов на 1

  }

  feet += dd1.feet + dd2.feet;           // сложение футов

}

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

int main()

{

  Distance dist1, dist3;        // две длины

  Distance dist2(11, 6.25);     // определение и инициализация

  dist1.getdist();              // ввод dist1

  dist3.add_dist(dist1,dist2);  // dist3 = dist1 + dist2

  // вывод всех длин

  cout << "\ndist1 = "; dist1.showdist();

  cout << "\ndist2 = "; dist2.showdist();

  cout << "\ndist3 = "; dist3.showdist();

  cout << endl;

  return 0;

}

Основной блок этой программы начинается с присвоения начальных значе- ний полям объекта dist2 класса Distance, после чего производится его сложение

с экземпляром dist1, инициализируемым пользователем. Затем на экран выво- дятся все три экземпляра класса Distance:

Введите число футов: 17 Введите число дюймов: 5.75 dist1 = 17'-5.75" dist2 = 11'-6.25" dist3 = 29'-О"

Рассмотрим теперь, как выполняются новые действия, включенные в программу.

Перегруженные конструкторы

Было бы удобно производить инициализацию переменных класса Distance в мо- мент их создания, то есть использовать объявления типа

 

Distance width(5,  6.25);

 

где определяется объект width, сразу же инициализируемый значениями 5 и 6.25 для футов и дюймов соответственно.

Чтобы сделать это, вызовем конструктор следующим образом:

 

Distance(int ft, float in) : feet(ft), inches(in) { }

 

Мы инициализируем поля feet и inches теми значениями, которые передают- ся конструктору в качестве аргументов.

Тем не менее, мы хотим сохранить возможность определять переменные клас- са Distance без инициализации, подобно тому, как мы делали в программе ENGLOBJ.

Distance dist1, dist2;

В программе ENGLOBJ не было конструктора, но определения работали без ошибок. Почему же они работали без конструктора? Это объясняется тем, что компилятор автоматически встраивает в программу конструктор без парамет- ров, который и создает переменные класса, несмотря на то, что явного определе- ния конструктора мы не делали. Такой конструктор без параметров называется конструктором по умолчанию. Если бы конструктор по умолчанию не создавал- ся автоматически, то мы не смогли бы определять переменные классов, в кото- рых отсутствует конструктор.

Зачастую нам хотелось бы, чтобы начальные значения полям объекта при- сваивались также и в конструкторе без параметров. Если возложить эту функ- цию на конструктор по умолчанию, то мы не сможем узнать, какими значения- ми были инициализированы поля. Если же для нас важно, какими значениями будут инициализироваться поля объекта класса, то нам следует явно опреде- лить конструктор. В программе ENGLCON мы поступаем подобным образом:

Distance() : feet(O), inches(O.O) //конструктор по умолчанию

{ }           //тело функции пусто, никаких действий не производится

Члены класса инициализируются константными значениями, в данном слу- чае целым значением 0 для переменной feet и вещественным значением 0.0 для переменной inches. Значит, мы можем использовать объекты, инициализиру-

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

Теперь у нас имеется два явно определенных конструктора с одним и тем же именем Distance(), и поэтому говорят, что конструктор является перегруженным. Какой из этих двух конструкторов исполняется во время создания нового объек- та, зависит от того, сколько аргументов используется при вызове:

Distance length;              //вызывает первый конструктор

Distance width(11, 6.0); //вызывает второй конструктор

Определение методов класса вне класса

До сих пор мы всегда определяли методы класса внутри самого класса. На самом деле это не является обязательным. В примере ENGLCON метод add_dist() опреде- лен вне класса Distance(). Внутри определения класса содержится лишь прототип функции add_dist();

void add_dist(Distance, Distance);

Такая форма означает, что функция является методом класса, однако ее оп- ределение следует искать не внутри определения класса, а где-то в другом месте листинга.

В примере ENGLCON функция add_dist() определена позже, чем класс Distance(). Ее код взят из программы  ENGLSTRC главы 4:

void Distance::add_dist(Distance dd1, Distance dd2)

{

  inches = dd1.inches + dd2.inches;      // сложение дюймов

  feet = 0;                              // с возможным заемом

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

  {                                      // то уменьшаем число дюймов

    inches -= 12.0;                      // на 12.0 и увеличиваем

    feet++;                              // число футов на 1

  }

  feet += dd1.feet + dd2.feet;           // сложение футов

}

Заголовок функции содержит не встречавшиеся нам ранее синтаксические элементы. Перед именем функции add_dist() стоит имя класса Distance и новый символ ::. Этот символ является знаком операции глобального разрешения. Такая форма записи устанавливает взаимосвязь функции и класса, к которой относит- ся эта функция. В данном случае запись Distance::add_dist() означает, что функ- ция add_dist() является методом класса Distance. Вышесказанное проиллюстри- ровано на рис. 6.5.

             void Distance::add_dist(Distance dd1, Distance dd2)

 

Рис. 6.5. Операция разрешения

 

14