Программирование >>  Оптимизация возвращаемого значения 

1 ... 68 69 70 [ 71 ] 72 73 74 ... 96


Продолжение класса String, const CharProxy

operator[] (int index) const; Для const String. CharProxy operator[](int index); Для не-const String.

friend class CharProxy; private:

RCPtr<StringValue> value;

Кроме добавления класса CharProxy (который будет рассмотрен ниже), этот luiacc String отличается от последней версии класса String из правила 29 только тем, что обе функции operator [ ] возвращают теперь объекты CharProxy. Но пользователи класса String могут игнорировать этот факт и писать программу как обычно: будто функции operator [ ] возвращают символы (или ссылки на символы - см. правило 1):

string si, s2; Строки с подсчетом ссылок,

использующие proxy-объекты.

cout si [5] ; Допустимо и работает.

s2[5] = х ; Также допустимо и работает.

sl[3] = s2[8]; Естественно, допустимо,

естественно, работает.

Интересно не то, что прием работает. Интересно, как это происходит. Рассмотрим вначале оператор

cout << si[5] ;

Выражение si [ 5 ] соответствует объекту CharProxy. Для таких объектов не определен оператор вывода, поэтому компиляторам придется найти неявное преобразование типов, которое они могут применить, чтобы сделать вызов функции operator успешным (см. правило 5). И они находят неявное преобразование из CharProxy в char, объявленное в классе CharProxy, и автоматически вызывают этот оператор преобразования, и в результате выводится символ строки, представленный объектом CharProxy. Это пример преобразования из CharProxy в char, которое выполняется для всех объектов CharProxy, используемых в качестве rvalue.

Использование их в качестве lvalue обрабатывается по-другому. Рассмотрим снова оператор

s2[5] = X ;

Выражение s2 [ 5 ], как и раньше, возвращает объект CharProxy, но на этот раз объект является целью присваивания. Какой оператор присваивания вызывается при этом? Цель присваивания - объект CharProxy, поэтому вызывается оператор присваивания класса CharProxy. Это важно, поскольку объект, принимающий новое значение в операторе присваивания CharProxy, используется как lvalue. Отсюда известно, что символ строки, замещаемый proxy-объектом,



функционирует как lvalue, и, следовательно, надо предпринять необходимые действия для реализации доступа к символу как к lvalue. Аналогично, оператор

sl[3] = s2[8];

вызывает оператор присваивания для двух объектов CharProxy, и в этом операторе объект слева используется как lvalue, а объект справа - как rvalue. Вот код для функций operator [ ] класса String:

const string::CharProxy String::operator[](int index) const {

return CharProxy(const cast<String&>{*this), index);

String::CharProxy String::operator[] (int index) {

return CharProxy{*this, index);

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

Обратите внимание, что const версия функции operator [] возвращает const proxy-объект. Поскольку функция-член CharProxy: : operator= не объявлена как const, такие proxy-объекты не могут использоваться в качестве цели присваивания. Следовательно, ни proxy-объект, возвращаемый const версией функции operator [ ], ни символ, который он замещает, не могут использоваться в качестве lvalue. Это в точности то поведение, которое и требуется от const версии operator [ ] в рассматриваемом примере.

Обратите внимание на использования для *this оператора const cast (см. правило 2) при создании объекта CharProxy, возвращаемого const operator [ ]. Это необходимо, чтобы удовлетворить ограничениям конструктора CharProxy, который принимает только не-const объекты String. Операторы приведения типов часто создают проблемы, но в этом сл5Д1ае объект CharProxy, возвращаемый функцией operator [ ], сам имеет атрибут const, поэтому нет опасности, что будет изменен объект типа string, содержащий символ, на который ссылается proxy-объект.

Каждый proxy-объект, возвращаемый функцией operator [ ], помнит, к какой строке он относится и индекс представляемого им символа в этой строке:

string::CharProxy::CharProxy(Strings str, int index) : theString(str), charlndex(index) {}

Преобразование proxy-объекта к типу rvalue выполняется несложно - надо просто возвратить копию символа, представленного proxy-объектом:

string::CharProxy::operator charO const {

return theString.value->data[charlndex];



Если вы забыли о связи между объектом string, его элементом value и элементом data, на который он указывает, обратитесь к правилу 29. Так как эта функция возвращает символ по значению, а С++ разрешает делать это только для значений, используемых в качестве rvalue, данная функция преобразования может использоваться только там, где допустимо использовать rvalue.

Таким образом, вы обращаетесь к реализации операторов присваивания класса CharProxy, в которых символ, представленный proxy-объектом, используется в качестве цели присваивания, то есть как lvalue. Можно реализовать обычный оператор присваивания класса CharProxy следующим образом:

string::CharProxy&

String::CharProxy::operator=(const CharProxySc rhs) {

Если значение строки используется совместно несколькими объектами String, создать собственную копию значения, if (theString.value->isShared()) {

theString.value = new StringValue(theString.value->data);

Выполнить присваивание: присвоить значение символа, представленного rhs, символу, представленному *this. theString.value->data[charlndex] =

rhs.theString.value->data[rhs.charlndex]; return *this;

Сравнивая ЭТОТ КОД С реализацией не-const функции String: : operator [] на странице 211, вы увидите, что они поразительно похожи. Этого и следовало ожидать. В правиле 29 предполагалось, что все вызовы не-const функции operator [ ] выполнялись для записи и обрабатывались соответственно. Здесь же код для записи перемещен в операторы присваивания класса CharProxy, что позволило избежать затрат на выполнение записи, если не-const функция operator [ ] используется только как rvalue. Кстати, обратите внимание, что эта функция требует доступа к закрытому элементу value класса String, поэтому класс CharProxy объявлен дружественным классу String.

Второй оператор присваивания класса CharProxy почти идентичен первому:

string::CharProxy& String::CharProxy::operator=(char c) {

if (theString.value->isShared()) {

theString.value = new StringValue(theString.value->data) ;

theString.value->data[charlndex] = c; return *this;

Как грамотный программист, вы, конечно же, чтобы избежать дублирования кода в этих двух операторах присваивания, поместили бы его в закрытую функцию - член класса CharProxy, которая вызывалась бы из обоих операторов, не так ли?



1 ... 68 69 70 [ 71 ] 72 73 74 ... 96

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