Программирование >>  Инициализация объектов класса, структура 

1 ... 237 238 239 [ 240 ] 241 242 243 ... 395


class String {

public:

оператор присваивания для char* Strings operator=( const char * );

...

private:

int size; char *string;

уже была объявлена в нашем классе:

Такой оператор реализуется следующим образом. Если объекту String присваивается нулевой указатель, он становится пустым . В противном случае ему присваивается

Strings String::operator=( const char *sobj ) {

sobj - нулевой указатель

if (! sobj ) {

size = 0; delete[] string; string = 0;

else {

size = strlen( sobj );

delete[] string;

string = new char[ size + 1 ]; strcpy( string, sobj );

return *this;

копия С-строки:

string ссылается на копию той С-строки, на которую указывает sobj. Почему на копию? Потому что непосредственно присвоить sobj члену string нельзя:

string = sobj; ошибка: несоответствие типов

sobj - это указатель на const и, следовательно, не может быть присвоен указателю на не-const (см. раздел 3.5). Изменим определение оператора присваивания:

Strings String::operator=( const *sobj ) { ... }

Теперь string прямо ссылается на С-строку, адресованную sobj. Однако при этом возникают другие проблемы. Напомним, что С-строка имеет тип const char*. Определение параметра как указателя на не-const делает присваивание невозможным:

car = Studebaker ; недопустимо с помощью operator=( char *) !

Итак, выбора нет. Чтобы присвоить С-строку объекту тина String, параметр должен иметь тип const char*.

м1 предоставляем оператор, принимающий параметр тина const char*. Эта операция



char ia[] = { d, a, n, c, e, r };

String trap = ia; trap. string ссылается на ia

символов, который модифицируется способом, неизвестным объекту String. Например:

ia[3] = g; а вот это нам не нужно:

модифицируется и ia, и trap. string

Если trap. string напрямую ссылался на ia, то объект trap демонстрировал бы своеобразное поведение: его значение может изменяться без вызова функций-членов класса String. Поэтому мы полагаем, что выделение области памяти для хранения копии значения С-строки менее опасно.

Обратите внимание, что в операторе присваивания используется delete. Член string содержит ссылку на массив символов, расположенный в хипе. Чтобы предотвратить утечку, память, выделенная под старую строку, освобождается с помощью delete до выделения памяти под новую. Поскольку string адресует массив символов, следует использовать версию delete для массивов (см. раздел 8.4).

И последнее замечание об операторе присваивания. Тип возвращаемого им значения -это ссылка на класс String. Почему именно ссылка? Дело в том, что для встроенных

сцепление операторов присваивания int iobj, jobj;

типов операторы присваивания можно сцеплять:

iobj = jobj = 63;

Они ассоциируются справа налево, т. е. в предыдущем примере присваивания выполняются так:

iobj = (jobj = 63);

Это удобно и при работе с объектами класса String: поддерживается, к примеру,

String ver, noun;

следующая конструкция:

verb = noun = count ;

При первом присваивании из этой цепочки вызывается определенный ранее оператор для const char*. Тип полученного результата должен быть таким, чтобы его можно было использовать как аргумент для копирующего оператора присваивания класса String. Поэтому, хотя параметр данного оператора имеет тип const char *, возвращается все же ссылка на String.

Операторы присваивания бывают перегруженными. Например, в нашем классе String есть такой набор:

Хранение в string прямой ссылки на С-строку, адресуемую sobj, порождает и иные сложности. М1 не знаем, на что именно указывает sobj. Это может быть массив



набор перегруженн операторов присваивания Strings operator=( const String s );

Strings operator=( const char * );

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

15.4. Оператор взятия индекса

Оператор взятия индекса operator[]() можно определять для классов, представляющих абстракцию контейнера, из которого извлекаются отдельные элементы. Примерами таких контейнеров могут служить наш класс String, класс IntArray, представленный в главе 2, или шаблон класса vector, определенный в стандартной библиотеке С++. Оператор взятия индекса обязан быть функцией-членом класса.

У пользователей String должна иметься возможность чтения и записи отдельных символов члена string. Мы хотим поддержать следующий способ применения объектов

String entry( extravagant ); String mycopy;

for ( int ix = 0; ix < entry.size(); ++ix )

данного класса:

mycopy[ ix ] = entry[ ix ];

Оператор взятия индекса может появляться как слева, так и справа от оператора присваивания. Чтобы быть в левой части, он должен возвращать l-значение

#include <cassert> inine chars

String::operator[]( int elem ) const {

assert( elem >= 0 ss elem < size ); return string[ elem ];

индексируемого элемента. Для этого м1 возвращаем ссылку:

String color( violet );

В следующем фрагменте нулевому элементу массива color присваивается символ V: color[ 0 ] = V;

Обратите внимание, что в определении оператора проверяется выход индекса за границы массива. Для этого используется библиотечная С-функция assert() . Можно также возбудить исключение, показывающее, что значение elem меньше 0 или больше длины С-



1 ... 237 238 239 [ 240 ] 241 242 243 ... 395

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