Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект_ООП_КЛАССЫ.doc
Скачиваний:
9
Добавлен:
12.02.2016
Размер:
10.12 Mб
Скачать

Определение класса Array.

Определения функций-элементов класса Array.

Драйвер для простого класса Array.

Результат выполнения программы:

Рисунок 3.2. Класс массив и его перегруженные операции

Далее программа проверяет перегруженную операцию проверки на равенство (= =), чтобы можно было убедиться, что объекты integers1иintegers2действительно идентичны после выполнения операции присваивания.

Затем программа использует перегруженную операцию индексации, чтобы найти по ссылке элемент integers[5], принадлежащийintegersl. Это индексированное имя затем используется как R-величина для печати значенияintegersl[5]и как L-величина с левой стороны оператора присваивания, чтобы присвоить новое значение 1000 элементуintegersl[5], Заметим, чтоoperator[]возвращает ссылку и присваивает значение.

Затем программа пытается присвоить значение 1000 элементу integеrsl[15], индекс которого находится вне допустимого диапазона. Программа отлавливает эту ошибку и аварийно завершается.

Интересно, что операция индексации массива не ограничивается приме­нением только к массивам; ее можно использовать для выделения элементов из других видов классов-контейнеров, таких, как связные списки, строки, словари и так далее. Кроме того, индексы – это не обязательно целые числа; можно использовать, например, символы и строки.

Теперь перей­дем к рассмотрению заголовка класса и определений функций-элементов. Строки

int *ptr; // указатель на первый элемент массива

int size; // размер массива

static int arrayCount; // число экземпляров массивов

представляют закрытые элементы класса. Массив состоит из указателя ptrна соответствующий тип (в данном случаеint), элементаsize,обозначающего коли­чество элементов в массиве, и статического элементаarrayCount, обозначающего количество экземпляров объектов массива, которые были образованы. Строки

friend ostream &operator<< (ostream &, const Array &);

friend istream &operator>>(istream &, Array &);

объявляют перегруженные операции поместить в поток и взять из потока друзьями класса Array.Когда компилятор видит выражение, подобное

cout << arrayObject

он активизирует функцию operator<<путем генерации вызова

operator<<(cout,arrayObject)

Когда компилятор видит выражение, подобное

cin >>arrayObject

он активизирует функцию operator>> путем генерации вызова

operator>>(cin, arrayObject)

Еще раз отметим, что эти функции-операции не могут быть элементами класса Array, так как объектArrayвсегда упоминается с правой стороны операций поместить в поток и взять из потока. Функцияoperator<<печатает число элементов, указанное с помощьюsize, из массива, начало которого хранится вptr.Функцияoperator>>непосредственно помещает вводимые значения в массив, указанный вptr. Каждая из этих функций-операций возвращает соответствующую ссылку, чтобы обеспечить возможность сцеп­ленных вызовов.

Строка

Array(int = 10); //конструктор с умолчанием

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

Array integers1(7);

или эквивалентную форму

Array integers1 = 7;

он активизирует конструктор с умолчанием. Функция-элемент конструктор с умолчанием класса Arrayувеличивает на 1arrayCount, копирует аргумент в элемент данныхsize, с помощьюnewвыделяет память для хранения внутрен­него представления этого массива и указатель, возвращенный операциейnew, присваивает элементу данныхptr, применяетassertдля проверки нормального завершенияnew, а затем использует циклforдля задания нулевых начальных значений элементам массива. Можно, конечно, сделать классArray, который не инициализирует свои элементы, если, например, эти элементы все равно должны будут позднее вводиться извне. Но это рассматривается, как плохой стиль программирования. Массив должен быть всегда должным образом инициализирован согласованными данными. Строка

Array (constArray&) ; //конструктор копии

является конструктором копии. Она задает начальные значения объекту класса Array путем копирования существующего объекта классаArray. Такое копирование должно выполняться весьма тщательно, чтобы избежать ловуш­ки, состоящей в том, что оба объекта типаArrayуказывают на одну и ту же динамически распределенную область памяти; точно такая же проблема воз­никла бы с побитовым копированием по умолчанию. Конструкторы копии вызываются всякий раз, когда возникает необходимость в копировании объ­екта, например, при вызове по значению, когда объект возвращается из вызванной функции, или при инициализации объекта, который должен быть копией другого объекта того же самого класса. Конструктор копии вызывается в объявлении, когда объект классаArrayсоздается и инициализируется другим объектом классаArray, как, например, в следующем объявлении:

Array integers3 (integers1);

или в эквивалентном объявлении

Array integers3=integers1;

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

Функция-элемент конструктор копии класса Arrayувеличивает на 1аrrayCount, копирует элементsizeмассива, использованного для инициализа­ции,в элемент данныхsizeнового объекта, с помощьюnewвыделяет память для размещения внутреннего представления массива и присваивает указа­тель, возвращенный операциейnew, элементу данныхptr, применяетassertдля проверки успешного завершенияnew, затем использует циклfor, чтобы скопировать все элементы инициирующего массива в новый массив. Важно отметить, что если бы конструктор копии просто скопировалptrиз исходного объекта в новый объект, то оба объекта указывали бы на одну и ту же динамически распределенную область памяти. Выполнение первого же де­структора в дальнейшем уничтожило бы динамически выделенную область памяти и указателиptrостальных объектов оказались бы неопределенными, что привело бы к ситуации, способной вызвать серьезную ошибку выполне­ния.

Строка

~Array(); //деструктор

объявляет деструктор класса. Деструктор активизируется автоматически по окончании жизни объекта класса Array. Деструктор уменьшает на 1arrayCount, затем используетdeleteдля освобождения динамической области памя­ти, созданной в конструкторе с помощьюnew. Строка

const Array &operator=(const Array &); //присваивание массивов

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

integersl = integers2;

он активизирует функцию operators=, генерируя вызов

integersl.operator=(integers2)

Функция элемент operator=сначала осуществляет проверку самоприсва­ивания. Если имеет место попытка самоприсваивания, присваивание пропус­кается (т.е. объект уже есть сам по себе). Если самоприкваивания нет, то функция-элемент используетdelete, чтобы освободить память, ранее выделенную в массиве-адресате, копируетsizeисходного массива вsizeмассива-адресата, используетnew, чтобы выделить требуемую память массиву-адресату, и помещает ука­затель, возвращенныйnew, в элементptr массива, используя при этомassertдля проверки успешного завершенияnew. Затем используется циклforпроверки успешного завершенияnew. Затем используется циклfor для копирования элементов исходного массива в массив-адресат. Независимо от того, есть самоприсваивание или нет, функция элемент затем возвращает текущий объект (т.е.*this) как константную ссылку; это делает возможным сцепленное присваивание, такое какx=y=z. Если бы проверки самоприсва­ивания не было, функция-элемент должна была бы начать с уничтожения пространства массива-адресата. Поскольку при самоприсваивании это также и исходный массив, то массив оказался бы разрушенным.

Строка

int operator= = (const Array &) const; // проверка равенства

объявляет перегруженную операцию проверки на равенство (= =) для класса Array. Когда компилятор встречает вmainвыражение вида

integers1 = = integers2

он активизирует функцию-элемент operator= =,генерируя вызов

integersl.operator= =(integers2)

Если элементы sizeмассивов различны, функция-элементoperator= =сразу возвращает 0 (ложь). В противном случае функция-элемент сравнивает каждую пару элементов. Если все они одинаковы, возвращается 1 (истина). Если обнаружилась первая пара различных элементов, это сразу же вызывает возврат 0 (ложь).

Строка

int operator!=(const Array &) const; //сравнение на неравенство

объявляет для класса перегруженную операцию проверки неравенства масси­вов, Функция-элемент operator!=активизируется и работает аналогично пере­груженной функции операции проверки равенства. Отметим, что перегруженная функция operator!=могла бы быть написана в терминах перегруженной функцииoperator= =следующим образом:

int Array: : operator!= (constArray&right) const

{ return !(*this = = right);!}

Это определение функции использует перегруженную функцию operator= =, чтобы определить, равен ли один массивArrayдругому, а затем воз­вращает отрицание этого результата. Создание функцииoperator!=этим спо­собом дает программисту возможность повторно использовать функциюoperator= =и уменьшить объем описания класса.

Строка

int &operator[](int); // операция индексации

объявляет для класса перегруженную операцию индексации. Когда компиля­тор встречает в main выражение

integersi[](5)

он активизирует перегруженную функцию-элемент operator[],генерируя вызов

integer1.operator [](5)

Функция-элемент operator[]проверяет, находится ли индекс в допусти­мом диапазоне, и если нет, то программа аварийно завершается. Если индекс в заданном диапазоне, то соответствующий элемент массива возвращается как ссылка, так что он может быть использован как L-величина (например, с левой стороны оператора присваивания).

Строка

static int getArrayCount( );//возвращение числа экземпляров

объявляет статическую функцию-элемент getArrayCount, которая возвращает значение статического элемента данныхarrayCount, даже если не существует ни одного объекта классаArray.

3.8. Преобразования типов

Большинство программ работает с информацией разных типов. Иногда все операции остаются «в пределах одного типа». Например, сложение целого и целого дает снова целое (до тех пор, пока результат не станет слишком большим для представления целым). Но часто возникает необходимость пре­образовать данные одного типа в данные другого типа. Это может случиться при присваиваниях, при вычислениях, при передаче значений функциям и возвращении значений от функций. Компилятор знает, как выполнить оп­ределенные преобразования между встроенными типами. Программист может регулировать преобразования встроенных типов путем приведения типов.

Но что делать с типами, определенными пользователем? Компилятор не может сам по себе знать, как выполнять преобразования между встроенными типами и типами, определенными пользователем. Программист должен явно указать, как выполнять такие преобразования. Эти преобразования могут быть выполнены с помощью конструкторов преобразований– конструкто­ров с единственным аргументом, которые преобразуют объекты разных типов (включая встроенные типы) в объекты данного класса.

Операция преобразования (называемая также операцией приведения) может быть использована для преобразования объекта одного класса в объект другого класса или в объект встроенного типа. Такая операция преобразования должна быть нестатической функцией-элементом; операция преобра­зования этого вида не может быть дружественной функцией.

Прототип функции

A: :operatorchar* ( ) const;

объявляет перегруженную функцию-операцию приведения для создания времен­ного объекта char*из объекта определенного пользователем типаА. Перегружен­ная функция-операция приведения не указывает тип возвращаемых величин, потому что тип возвращаемых величин – это тип, к которому преобразован объект. Еслиs– объект класса, то когда компилятор встречает выражение(char*)s, он порождает вызовs.operator char*( ). Операндs– этo объект классаs, для которого была активизирована функция-элементoperator char*.

Перегруженная функция-операция приведения может быть определена для преобразования объектов определенных пользователем типов в объекты встроенных типов или в объекты других определенных пользователем типов. Прототипы

А::operator int ( ) const;

A::operator otherClass( ) const;

объявляют перегруженные функции-операции приведения для преобразования объекта определенного пользователем типа Ав целое и для преобразования объекта определенного пользователем типаА в объект другого определенного пользователем типаotherClass.

Одной из приятных особенностей операций приведения и конструкторов преобразований является то, что при необходимости компилятор может вы­зывать эти функции автоматически для создания временных объектов. Например, если в программе в том месте, где нормально ожидается char*,по­явился объектsопределенного пользователем типаString, например, в операторе

cout <<s;

то в этом случае компилятор вызывает перегруженную функцию-операцию приведения operator char*для преобразования объекта вchar*и использует в выражении результирующийchar*.Эта операция приведения для нашего классаStringпозволяет не перегружать операцию поместить в поток, предна­значенную для выводаStringс использованиемcout.

СПИСОК ИСПОЛЬЗОВАННОЙ ЛИТЕРАТУРЫ

  1. Дейтел Харви, Дейтел Пол. Как программировать на С++: Третье издание. Пер. с англ. – М.: ЗАО «Издательство БИНОМ», 2003. – 1152 с.

  2. Фридман А.Л. Язык программирования Си++. – М.: ИНТУИТ.РУ «Интернет-университет Информационных Технологий», 2003. – 288 с.

  3. Березин Б.И., Березин С.Б. Начальный курс С и С++. – М.: ДИАЛОГ-МИФИ, 2003. – 288 с.

  4. Франка П. С++: учебный курс. – СПб.: Питер, 2003. – 521 с.

  5. Дейл Н., Уимз Ч., Хедингтон М. Программирование на С++: Пер. с англ. – М.: ДМК, 2000. – 672 с.

  6. Страуструп Б. Язык программирования С++. / Пер.с англ. М.: Радио и связь, 1991. –352 с.

71