Лекции.ИНФО


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



Например, у нас определен такой класс.

class cl {

 int i;

 void f() { ... };

 // . . .

};

В функции f() можно использовать следующую инструкцию для присваивания члену i значения 10.

i = 10;

В действительности предыдущая инструкция представляет собой сокращенную форму следующей.

this->i = 10;

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

#include <iostream>

using namespace std;

class cl {

  int i;

 public:

  void load_i(int val) { this->i = val; } // то же самое, что i = val

  int get_i() { return this->i; } // то же самое, что return i

};

Int main()

{

 cl o;

 o.load_i (100);

 cout << о.get_i();

 return 0;

}

При выполнений эта программа отображает число 100.

Безусловно, предыдущий пример тривиален, но в нем показано, как можно использовать указатель this. Скоро вы поймете, почему указатель this так важен для программирования на C++.

Важно! Функции-"друзья" не имеют указателя this, поскольку они не являются членами класса. Только функции-члены имеют указатель this.


Глава 13: Перегрузка операторов

В C++ операторы можно перегружать для "классовых" типов, определяемых программистом. Принципиальный выигрыш от перегрузки операторов состоит в том, что она позволяет органично интегрировать новые типы данных в среду программирования.

Перегружая оператор, можно определить его значение для конкретного класса. Например, класс, который определяет связный список, может использовать оператор "+" для добавления объекта к списку. Класс, которые реализует стек, может использовать оператор "+" для записи объекта в стек. В каком-нибудь другом классе тот же оператор "+" мог бы служить для совершенно иной цели. При перегрузке оператора ни одно из оригинальных его значений не теряется. Перегруженный оператор (в своем новом качестве) работает как совершенно новый оператор. Поэтому перегрузка оператора "+" для обработки, например, связного списка не приведет к изменению его функции (т.е. операции сложения) по отношению к целочисленным значениям.

Перегрузка операторов тесно связана с перегрузкой функций. Чтобы перегрузить оператор, необходимо определить значение новой операции для класса, к которому она будет применяться. Для этого создается функция operator (операторная функция), которая определяет действие этого оператора. Общий формат функции operator таков.

тип имя_класса::operator#(список_аргументов)

{

 операция_над_классом

}

Операторы перегружаются с помощью функции operator.

Здесь перегружаемый оператор обозначается символом "#", а элемент тип представляет собой тип значения, возвращаемого заданной операцией. И хотя он в принципе может быть любым, тип значения, возвращаемого функцией operator, часто совпадает с именем класса, для которого перегружается данный оператор. Такая корреляция облегчает использование перегруженного оператора в составных выражениях. Как будет показано ниже, конкретное значение элемента список_аргументов определяется несколькими факторами.

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

Перегрузка операторов с использованием функций-членов

Начнем с простого примера. В следующей программе создается класс three_d, который поддерживает координаты объекта в трехмерном пространстве. Для класса three_d перегружаются операторы "+" и "=". Итак, рассмотрим внимательно код этой программы.

// Перегрузка операторов с помощью функций-членов.

#include <iostream>

using namespace std;

class three_d {

  int x, y, z; // 3-мерные координаты

 public:

  three_d() { x = у = z = 0; }

  three_d(int i, int j, int k) {x = i; у = j; z = k; }

  three_d operator+(three_d op2); // Операнд op1 передается неявно.

  three_d operator=(three_d op2); // Операнд op1 передается неявно.

  void show();

};

// Перегрузка оператора "+".

three_d three_d::operator+(three_d op2)

{

 three_d temp;

 temp.x = x + op2.x; // Операции сложения целочисленных

 temp.у = у + ор2.у; // значений сохраняют оригинальный

 temp.z = z + op2.z; // смысл.

 return temp;

}

// Перегрузка оператора присваивания.

three_d three_d::operator=(three_d op2)

{

 x = op2.x; // Операции присваивания целочисленных

 у = ор2.у; // значений сохраняют оригинальный

 z = op2.z; // смысл.

 return *this;

}

// Отображение координат X, Y, Z.

void three_d::show()

{

 cout << x << ", ";

 cout << у << ", ";

 cout << z << "";

}

Int main()

{

 three_d a(1, 2, 3), b(10, 10, 10), c;

 a.show();

 b.show();

 c=a+b; // сложение объектов а и b

 c.show();

 c=a+b+c; // сложение объектов a, b и с

 с.show();

 c=b=a; // демонстрация множественного присваивания

 с.show();

 b.show();

 return 0;

}

При выполнении эта программа генерирует такие результаты.

1, 2, 3

10, 10, 10

11, 12, 13

22, 24, 26

1, 2, 3

1, 2, 3

Исследуя код этой программы, вы, вероятно, удавились, увидев, что обе операторные функции имеют только по одному параметру, несмотря на то, что они перегружают бинарные операции. Это, на первый взгляд, "вопиющее" противоречие можно легко объяснить. Дело в том, что при перегрузке бинарного оператора с использованием функции-члена ей передается явным образом только один аргумент. Второй же неявно передается через указатель this. Таким образом, в строке

temp.x = х + ор2.х;

под членом х подразумевается член this->x, т.е. член х связывается с объектом, который вызывает данную операторную функцию. Во всех случаях неявно передается объект, указываемый слева от символа операции, который стал причиной вызова операторной функции. Объект, располагаемый с правой стороны от символа операции, передается этой функции в качестве аргумента. В общем случае при использовании функции-члена для перегрузки унарного оператора параметры не используются вообще, а для перегрузки бинарного — только один параметр. (Тернарный оператор "?" перегружать нельзя.) В любом случае объект, который вызывает операторную функцию, неявно передается через указатель this.

Чтобы понять, как работает механизм перегрузки операторов, рассмотрим внимательно предыдущую программу, начиная с перегруженного оператора "+". При обработке двух объектов типа three_d оператором "+" выполняется сложение значений соответствующих координат, как показано в функции operator+(). Но заметьте, что эта функция не модифицирует значение ни одного операнда. В качестве результата операции эта функция возвращает объект типа three_d, который содержит результаты попарного сложения координат двух объектов. Чтобы понять, почему операция "+" не изменяет содержимое ни одного из объектов-участников, рассмотрим стандартную арифметическую операцию сложения, примененную, например, к числам 10 и 12. Результат операции 10+12 равен 22, но при его получении ни 10, ни 12 не были изменены. Хотя не существует правила, которое бы не позволяло перегруженному оператору изменять значение одного из его операндов, все же лучше, чтобы он не противоречил общепринятым нормам и оставался в согласии со своим оригинальным назначением.

Обратите внимание на то, что функция operator+() возвращает объект типа three_d. Несмотря на то что она могла бы возвращать значение любого допустимого в C++ типа, тот факт, что она возвращает объект типа three_d, позволяет использовать оператор "+" в таких составных выражениях, как a+b+с. Часть этого выражения, а+Ь, генерирует результат типа three_d, который затем суммируется с объектом с. И если бы эта часть выражения генерировала значение иного типа (а не типа three_d), такое составное выражение попросту не работало бы.

В отличие от оператора "+", оператор присваивания приводит к модификации одного из своих аргументов. (Прежде всего, это составляет саму суть присваивания.) Поскольку функция operator=() вызывается объектом, который расположен слева от символа присваивания (=), именно этот объект и модифицируется в результате операции присваивания. После выполнения этой операции значение, возвращаемое перегруженным оператором, содержит объект, указанный слева от символа присваивания. (Такое положение вещей вполне согласуется с традиционным действием оператора "=".) Например, чтобы можно было выполнять инструкции, подобные следующей

а = b = с = d;

необходимо, чтобы операторная функция operator=() возвращала объект, адресуемый указателем this, и чтобы этот объект располагался слева от оператора "=". Это позволит выполнить любую цепочку присваиваний. Операция присваивания — это одно из самых важных применений указателя this.

Узелок на память. Если для перегрузки бинарного оператора используется функция-член, объект, стоящий слева от оператора, вызывает операторную функцию и передается ей неявно через указатель this. Объект, расположенный справа от оператора, передается операторной функции как параметр.

Использование функций-членов для перегрузки унарных операторов

Можно также перегружать такие унарные операторы, как "++", "--", или унарные "-" и "+". Как упоминалось выше, при перегрузке унарного оператора с помощью функции-члена операторной функции ни один объект не передается явным образом. Операция же выполняется над объектом, который генерирует вызов этой функции через неявно переданный указатель this. Например, рассмотрим расширенную версию предыдущего примера программы. В этом варианте для объектов типа three_d определяется операция инкремента.

// Перегрузка унарного оператора.

#include <iostream>

using namespace std;

class three_d {

  int x, y, z; // 3-мерные координаты

 public:

  three_d() { x = у = z = 0; }

  three_d(int i, int j, int k) {x = i; у = j; z = k; }

  three_d operator+(three_d op2); // Операнд op1 передается неявно.

  three_d operator=(three_d op2); // Операнд op1 передается неявно.

  three_d operator++(); // префиксная версия оператора ++

  void show();

};

// Перегрузка оператора " + ".

three_d three_d::operator+(three_d op2)

{

 three_d temp;

 temp.x = x + op2.x; // Операции сложения целочисленных

 temp.у = у + ор2.у; // значений сохраняют оригинальный

 temp.z = z + op2.z; // смысл.

 return temp;

}

// Перегрузка оператора присваивания.

three_d three_d::operator=(three_d op2)

{

 x = op2.x; // Операции присваивания целочисленных

 у = ор2.у; // значений сохраняют оригинальный

 z = op2.z; // смысл.

 return *this;

}

// Перегруженная префиксная версия оператора "++".

three_d three_d::operator++()

{

 х++; // инкремент координат х, у и z

 у++;

 z++;

 return *this;

}

// Отображение координат X, Y, Z.

void three_d::show()

{

 cout << x << ", ";

 cout << у << ", ";

 cout << z << "";

}

Int main()

{

 three_d a(1, 2, 3), b(10, 10, 10), c;

 a.show();

 b.show();

 с = a + b; // сложение объектов а и b

 c.show();

 c=a+b+c; // сложение объектов a, b и с

 с.show();

 с = b = a; // множественное присваивание

 с.show();

 b.show();

 ++c; // инкремент с

 c.show();

 return 0;

}









Читайте также:

Последнее изменение этой страницы: 2016-03-17; Просмотров: 130;


lektsia.info 2017 год. Все права принадлежат их авторам! Главная