Программирование >>  Структурное программирование 

1 ... 175 176 177 [ 178 ] 179 180 181 ... 342


объявляют закрытые элементы класса. Объект String имеет указатель на динамически выделенную память для хранения строки символов, и имеет поле длины, которое хранит количество символов в строке без учета нулевого символа в конце.

Теперь пройдемся по заголовочному файлу класса String на рис. 8.5. Строки

friend ostream &operator (ostream &, const String &); friend istream &operator (istream &, const String &);

объявляют перегруженные функции-операции поместить в поток operator и взять из потока operator друзьями класса. Реализация этого очевидна. Строка

String(const char * = ); конструктор преобразования

объявляет конструктор преобразования. Этот конструктор берет аргумент char* (это по умолчанию пустая строка) и создает объект String, который включает эту строку символов. Любой конструктор с единственным аргументом можно рассматривать как конструктор преобразования. Как мы увидим, такие конструкторы полезны, когда мы выполняем любую операцию со String, используя аргументы char*. Конструктор преобразования преобразует соответствующую строку в объект String, который затем присваивается объекту-адресату String. Наличие этого конструктора преобразования означает, что нет необходимости применять перегруженную операцию специально для присваивания строк символов объектам String. Компилятор автоматически активизирует конструктор преобразования для создания временного объекта String, содержащего строку символов. Затем активизируется перегруженная операция присваивания, чтобы присвоить временный объект String другому объекту String.

Замечание по технике программирования 8.6

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

Конструктор преобразования String может быть активизирован, например, таким объявлением: String 81( поздравляем ). Функция-элемент конструктор преобразования вычисляет длину строки символов и присваивает эту длину закрытому элементу данных length, использует new для присваивания значения указателя на выделенный необходимый объем памяти закрытому элементу данных sPtr, применяет assert для проверки успешного выполнения new и, если все нормально, использует strcpy для копирования строки символов в объект.

Строка

String(const String &); конструктор копии

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



деленную область памяти; такая проблема возникает, в частности, при побитовом копировании, осуществляемом по умолчанию. Конструктор копии работает аналогично конструктору преобразования, за исключением того, что он просто копирует элемент length из исходного объекта String в объект-адресат String. Заметим, что конструктор копии выделяет новую область памяти для внутреннего представления строки символов объекта-адресата. Если бы он просто копировал sPtr в исходном объекте в sPtr объекта-адресата, то оба объекта указывали бы на одну и ту же динамически распределенную область памяти. Выполнение первого же деструктора в дальнейшем уничтожило бы динамически выделенную область памяти и указатели ptr остальных объектов оказались бы неопределенными, что привело бы к ситуации, способной вызвать серьезную ошибку выполнения. Строка

-string О; деструктор

объявляет деструктор класса String. Деструктор использует delete для освобождения области динамически распределенной памяти, выделенной new под хранение строки символов. Строка

const String &operator=(const String &); присваивание

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

stringl.operator=(string2) ;

Перегруженная функция-операция присваивания operator= сначала проверяет, не осуществляется ли самоприсваивание. Если должно осуществляться самоприсваивание, функция просто возвращается, потому что объект уже существует сам по себе. Если бы эта проверка пропускалась, функция сразу же освобождала бы область памяти в объекте-адресате и поэтому теряла бы строку символов. Если производится не самоприсваивание, функция освобождает область памяти, копирует поле длины исходного объекта в объект-адресат, создает новую область памяти в объекте-адресате, использует assert для проверки успешного выполнения new и затем использует strcpy для копирования строки символов из исходного объекта в объект-адресат. Независимо от того, было или не было самоприсваивание, возвращается *this для того, чтобы обеспечивать сцепленные присваивания.

Строка

string &operator+=(const String &); сцепление (конкатенация)

объявляет перегруженную операцию сцепления строк (конкатенации). Когда компилятор встречает в main выражение si -f-= s2, генерируется вызов функции si.operator-f-=(s2). Функция operator-f-= создает временный указатель, чтобы сохранять строку символов текущего объекта до тех пор, пока не появится возможность освободить занимаемую ею память, вычисляет общую длину сцепленной строки, использует new, чтобы зарезервировать место для суммарной строки, использует assert для проверки успешного выполнения new, использует strcpy для копирования исходной строки в заново выделенную область, использует strcat для сцепления строки символов исходного объекта с новой строкой, использует delete, чтобы освободить область памяти, занятую исходной строкой символов этого объекта, и возвращает *this как String &, чтобы обеспечить сцепленные операции -f-=.



Нужна ли нам вторая перегруженная операция сцепления объекта типа String не с другим объектом того же типа, а со строкой типа char*? Нет. Конструктор преобразования const char* преобразует строку во временный объект класса String, с которым затем и выполняется описанная перегруженная операция сцепления. С-Ы- может выполнять такие преобразования, но только на глубину одного уровня. С++ может также выполнять неявное определенное компилятором преобразование между встроенными типами перед выполнением преобразования между встроенными типами и классами. Отметим, что при создании временного объекта класса String вызываются конструктор преобразования и деструктор (смотри на рис.8.5 результирующие выходные данные, начиная с sl+= Bac дает результат). Это пример скрытых от клиента класса накладных расходов вызовов функций при создании временных объектов класса и уничтожении их во время неявных преобразований. Аналогичные накладные расходы создаются конструкторами копий при передаче им параметров вызовом по значению и при возвращении объектов класса по значению.

Совет по повышению эффективности 8.2

Наличие перегруженной операции сцепления +=, которая получала бы один аргумент типа const char*, более эффективно, чем выполнение сначала неявного преобразования, а затем сцепления. С другой аороны, неявные преобразования требуют меньшего объема кодирования и вызывают меньше ошибок.

Строка

int operator!() const; String пуст?

объявляет перегруженную операцию отрицания. Эта операция с классами строк обычно используется для проверки, пуста ли строка. Например, когда компилятор встречает выражение tstringl, он генерирует вызов функции

stringl.operator!()

Эта функция просто возвращает результат проверки, не равняется ли length нулю. Строки

int operator==(const String &) const; проверка si == s2

int operator!=(const String &) const; проверка si != s2

int operator< (const String &) const; проверка si < s2

int operator>(const String &) const; проверка si > s2

int operator>=(const String &) const; проверка si >= s2

int operator<=(const String &) const; проверка si <= s2

объявляют перегруженные операции проверки на равенство и отношения для класса String. Аналогичность всех этих операций позволяет нам ограничиться рассмотрением только одного примера, а именно, перегрузкой операции >=. Когда компилятор встречает выражение вида stringl >= string2, он генерирует вызов функции

stringl.operator>=(string2)

которая возвращает 1 (истина), если stringl больше или равна string2. Каждая из рассматриваемых операций использует stremp для сравнения строк символов в объектах типа String, Подчеркнем, что каждая из операций отношения



1 ... 175 176 177 [ 178 ] 179 180 181 ... 342

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика