Объектно-ориентированное программирование, страница 10

Vector Vector:: operator++(int);

friend Vector operator++(Vector& v, int);

определяют постфиксную модификацию при переопределении операции ++ методом класса или внешней функцией. Если реализация тела префиксной операции ++ очевидна, то постфиксная модификация требует пояснений. Возвращаемое функцией значение вектора должно быть старым, а содержимое объекта, пославшего сообщение, следует изменить на новое, вычисленное в соответствии с приведенными формулами. Таким образом, описание класса Vector можно дополнить двумя методами:

Vector operator++()     // Префиксная ++

{

  double d=!*this,   dn=(d+1.)/d;

  x1 *= dn;        x2 *= dn;

  return *this;

}

Vector operator++(int)  // Постфиксная ++

{

  Vector temp=*this;     // Запоминаем старый вектор

  double d=!*this,   dn=(d+1.)/d;

  x1 *= dn;  x2 *= dn;  // Новые значения компонент

  return temp;           // Результат выражения - старый

}

Обратите внимание: d=!*this; вычисляет норму вектора, пославшего сообщение. Здесь * — означает разадресацию, ! — означает уже переопределенную операцию взятия нормы. Для проверки функционирования вновь введенных операций в конец функции main можно добавить строки:

a=++b;

printf ("\n Norm of a=++b:\t\t %6.2f",!a);

b=a++;

printf ("\n Norm of b=a++:\t\t %6.2f",!b);

printf ("\n After a++ the norm of a:%6.2f",!a);

Если постфиксная модификация работает корректно, то последнее выведенное число должно быть на единицу больше, чем предпоследнее. Операция -- может быть переопределена аналогичным образом. Класс Vector выглядит далеко не полным. Разумно было бы дополнить его методами вычисления угла между двумя векторами, вычисления векторного произведения и другими. Не хватает также методов для ввода и вывода компонент вектора. Последние эффективно могут быть выполнены с помощью процедур библиотеки iostream.h, которые мы рассмотрим ниже.

Вопросы и упражнения

Можно ли в функции operator=() изменить тип возвращаемого значения на Vector&? Что при этом изменится? Будет ли работать программа, если функцию operator=() переписать в виде:

void operator=(Vector& v) { x1=v.x1; x2=v.x2; };

Будет ли работать цепочка присвоений c=a=a; в условиях предыдущего вопроса? Почему после присвоения b=a++; значения переменных a и b не равны? Докажите анализом кодов функции operator++().

Наследование данных и методов

Гибкость языка программирования в значительной степени определяется трудоемкостью подстройки существующих программных модулей к новым требованиям заказчика ПО. Степень полезности уже разработанных частей ПО значительно повышается, если существует возможность, не изменяя того, что написано, добавлять к ним новые свойства, модифицировать их функционирование или убирать свойства, ставшие ненужными, наследуя при этом большую часть свойств. В языке Smalltalk, так же как и в C++ в распоряжение программиста предоставлена целая иерархия классов и подклассов. Большинство программ на этих языках пишется с использованием сотен существующих классов и тысяч методов, которые объединены в библиотеки. Появляется возможность создавать объекты существующих классов, и посылать им сообщения в виде методов, или создавать классы, производные от существующих, изменив в них то, что надо, и работать с объектами этих новых классов. Бурно развивающийся С++ предоставляет широкие возможности для создания иерархии классов и подклассов. Библиотеки С++ интенсивно разрабатываются, многие из них уже зарекомендовали себя, как мощные и универсальные средства разработки ПО. Упомянем наиболее известные библиотеки классов: Borland OWL (Object Windows Library), MFC (Microsoft Foundation Classes) Windows Foundation Classes for Java (WFC). Следует отметить, что для использования всей мощи какой-либо библиотеки требуются значительные усилия, временные затраты и достаточно высокая квалификация пользователя, владеющего технологией ООП.

Производные классы в С++

Производный (Derived или Child) класс является основой для реализации механизма наследования свойств базового (Base или Parent) класса, а также средством подстройки класса к нуждам пользователя. Первые версии языка С++ позволяли подклассу иметь только одного родителя, один базовый класс. Теперь подкласс может наследовать черты нескольких базовых классов, что существенно увеличивает гибкость и мощь механизма наследственности. Подкласс (или производный класс) наследует все данные и методы базового класса. В дополнение к ним он может приобрести новые данные и методы, не содержащиеся в базовом классе, но необходимые для конкретных целей, преследуемых при создании подкласса. Каждый объект производного класса содержит свои собственные копии данных, унаследованных от базового класса. Кроме того, в нем обычно декларируются какие-то другие, новые данные, отсутствующие в базовом классе. Любой класс может стать базовым, если создать новый класс, объявив его производным от первого. Рассмотрим пример, в котором три класса образуют иерархию классов (рис. П.3). Здесь класс Man является родительским для двух других классов Stud и Prof.

Заметьте, что стрелки, как в генеалогии, принято проводить в направлении от дочерних классов к родительским. Оба производных класса получили в наследство от класса Man по две переменных (имя и возраст), но далее они завели себе дополнительные другие переменные: Stud — номер курса (course), а Prof — количество статей и название кафедры (publ и dept). Реализуем это в виде кодов.

//============== Global types and variables ===================//

enum eSortBy { None, ByName, ByAge, ByStatus };

const int gNameSize = 15, gDeptSize = 20, gBufSize = 256;

char buf[gBufSize];

//===================== Global funcs ======================//

void Clear (istream& is) //------- Clear keyboard buffer

{

if (cin.eof())

  return;

int n = is.rdbuf()->in_avail(); // How many bytes left?

while (n--)         // Eat out all of them

  is.rdbuf()->sbumpc();

is.clear();         // Clear the failbit

}

int In (string s, int min, int max) //------- Safe Input

{