
Штерн В. - Основы C++. Методы программной инженерии - 2003
.pdfГлава 16 • Расширенное использование перегрузки операций |
721 |
элемента данных str на эту заново выделенную память и динамически инициали зирует выделенную память, используя текстовый массив, предоставленный как аргумент.
void |
String::set(Gonst char* s) |
|
|
|||
{ |
size = strlen(s); |
|
/ / |
оценка размера |
||
|
str |
= new char[size |
+ 1 ] ; |
/ / |
запрос памяти динамически |
|
|
|
|
|
|
/ / |
распределяемой области |
|
i f |
(str == 0) |
{ cout |
« "Out of |
memory\n"; exit(O); } |
|
|
strcpy(str,s); |
} |
|
/ / |
копирование клиентских данных в "кучу" |
Это типичная структура динамического управления памятью. Такая закрытая функция удобна, потому что она инкапсулирует операции, обилие д/1Я конструкто ров и оператора присваивания.
Как подобает классу, который динамически управляет своей памятью, класс String предоставляет конструктор преобразования (он дублируется как конструк тор по умолчанию), конструктор копирования, оператор присваивания и деструк тор. Конструктор по умолчанию передает set() пустую строку. Конструктор преобразования отправляет set() как параметр в свой массив символов. Кон структор копирования передает set() в динамически распределяемую область памяти своего объекта-параметра. Деструктор возвращает память динамически распределяемой области памяти, которую конструктор или оператор присваива ния выделил для объекта String.
Такое добавление функций-членов поддерживает использование объектов String в разнообразных контекстах. Клиентская программа может определить объект String как неинициализированную переменную (вызывается конструктор по умолчанию), как объект, инициализированный значением символьного массива (применяется конструктор преобразования), или как объект, инициализирован ный данными другого существующего объекта String (вызывается конструктор копирования).
Оператор присваивания поддерживает присваивание объекта для самого себя. Он освобождает существующую память динамически распределяемой области памяти и выделяет и инициализирует новую память динамически распределяемой области памяти с помощью set(). Он поддерживает клиентскую программу, ко торая использует синтаксис выражения для нескольких последовательных при сваиваний (возвращая ссылку на целевой объект String). Обратите внимание, что хотя тело присваивания возвращает полный объект String (в данном случае разыменованный указатель), это только ссылка на возвращаемый объект — копирование отсутствует.
String& |
String::operator |
= (const |
String& s) |
||
{ i f (this |
== &s) return |
*this; |
/ / |
ничего, если поддерживается |
|
|
|
|
|
/ / |
самоприсваивание |
delete |
[ |
] str; |
|
/ / |
возвращение существующей памяти |
set(s . str); |
|
/ / |
выделение/установка новой памяти |
||
return |
*this; } |
|
/ / |
для поддержки цепочечного присваивания |
В листинге 16.3 показана полная реализация класса String вместе с встроен ными функциями-членами getSizeO и reset(). Первая функция возвращает максимальное количество символов, которые может содержать объект String. Вторая функция возвращает указатель на внутреннюю строку так, что клиентская программа (функции printStringO и modifyStringO) может инициализироват-. внешний указатель, который ссылается на внутреннюю строку.
Эти две клиентские функции используют операцию инкремента для поис:. • и замены символов внутри их параметра объектов String. Цикл в printString, продолжается до тех пор, пока во внутренней строке параметра String не встр.. тится оконечный нуль. (Указатель р на символ внутренней строки должен бьпь
|
Глава 16 • Расширенное использование перегрузки операций |
729 |
|||
String& String::operator = (const String& s) |
|
||||
{ |
if (this ==&s) return *this; |
// ничего, если самоприсваивание |
|
||
|
delete [ ] str; |
|
// возвращение существующей памяти |
|
|
|
set (s.str); |
|
// выделить/установить новую память |
|
|
|
return *this; } |
|
// для поддержки цепочечных присваиваний |
|
|
int String::getSize() const |
// нет изменений вобъекте String |
|
|||
{ |
return size; } |
|
// не константа: объект изменяется |
|
|
char* String::resetO |
|
|
|||
{ |
ptr = str; |
|
// установить текущий указатель наначало |
|
|
|
return str; } |
|
// возвратить указатель на начало |
|
|
char* String::operator ++() |
// приращение, затем доступ |
|
|||
{ |
if (ptr-str < size) |
// проверка наличия памяти |
|
||
|
return ++ptr; |
|
// указатель наследующий символ |
|
|
|
else |
|
// установить оконечный нуль |
|
|
|
{ *ptr = 0; |
} |
|
||
|
return ptr; } |
// не передавать, если конец |
|
||
char* String::operator ++(int) |
// доступ, затем приращение |
|
|||
{ |
if (ptr-str < size) |
// проверка наличия памяти |
|
||
|
return ptr++; |
|
// указатель наследующий символ |
|
|
|
else |
|
// установить оконечный нуль |
|
|
|
{ *ptr = 0; |
} |
|
||
|
return ptr; } |
// не передавать, если конец |
|
||
void printString(String& data) |
// не константа: строка изменяется |
|
|||
{ |
char *p =data.resetO; |
// указатель на первый символ |
|
||
|
while (*p != 0) |
|
// переход, пока необнаружится конец символов |
||
|
{ cout « *p; |
|
// вывод на печать текущего символа |
|
|
|
p =++data; } |
|
// указатель наследующий символ |
|
|
|
cout « endl; } |
|
|
|
|
void modifyString(String& data, const char text[]) |
|
||||
{ |
data.resetO; |
|
// указатель на первый символ |
|
|
|
int len = strlen(text) + 1 ; |
// установить предел итерации |
|
||
|
for (int i=0; i < len; i++) |
//просмотр каждого символа |
|
||
|
*data++ =text[i]; } |
//замечательный синтаксис: копирование символа |
|||
int mainO |
|
|
|
|
|
{ |
String data = "Hello World!"; |
|
|
|
|
|
/ / |
хороший вывод |
|
||
|
printString(data) |
|
|
||
|
modifyString(data |
How is the weather?"); |
|
|
|
|
printString(data) |
|
/ / |
память HE искажается |
|
|
return .0; |
|
|
|
|
|
} |
|
|
|
|
Как и в случае префиксных операций инкремента и декремента, перегружен ные постфиксные операции улучшают внешний вид программы, но не очень важ ны с точки зрения программной инженерии.
Операции преобразования
Приведение значения одного типа к значению другого типа достигается за счет применения имени целевого типа для значения (для имени переменной) исходного типа. Синтаксис бывает двух видов: традиционный и новый функционально-подоб ный. В традиционном синтаксисе имя типа цели (в скобках) используется перед