|
Программирование >> Оптимизация возвращаемого значения
Продолжение класса 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, которая вызывалась бы из обоих операторов, не так ли?
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |