|
Программирование >> Оптимизация возвращаемого значения
Правило 30 Ограничения Использование proxy-классов - удобный способ различать использование operator [ ] в качестве lvalue и rvalue, но этот метод не лишен недостатков. Конечно, было бы великолепно, если бы proxy-объекты незаметно замещали объекты, которые они представляют, однако идеала достичь нелегко. Это связано с тем, что объекты могут использоваться как lvalue не только в операторе присваивания, и такое использование proxy-объектов часто приводит к поведению, отличному от поведения настоящих объектов. Рассмотрим снова фрагмент кода из правила 29, из-за которого было принято решение добавить к объектам StringValue флаг, указывающий на возможность совместного использования. Если функция string::operator [ ] возвращает CharProxy вместо char&, то следующий код не будет больше компилироваться: string si = Hello ; char *p = &sl[l]; Ошибка! Выражение si [ 1 ] возвращает proxy-объект CharProxy, поэтому выражение справа от знака равенства (=) будет иметь тип CharProxy*. Преобразование из типа CharProxy* в char* не определено, из-за чего код инициализации р не будет компилироваться. Вообще, адрес proxy-объекта имеет тип, отличный от адреса настоящего объекта. Чтобы устранить этот недостаток, необходимо перегрузить операторы адресации для класса CharProxy: class String { public: class CharProxy { public: char * operators{); const char * operators{) const; Реализовать эти функции несложно. Функция const просто возвращает указатель на const версию представленного proxy-объектом символа: const char * String::CharProxy::operators{) const { return S{theString.value->data[charIndex]); He-const функция потребует несколько больше усилий, поскольку она возвращает указатель на символ, который может быть изменен. Ее поведение аналогично поведению не-const версии функции String::operator [ ] в правиле 29, и реализация также аналогична: char * String: :CharProxy::operators: () { Убедиться, что символ, указатель на который возвращает эта функция, не используется другими объектами String, if (theString.value->isShared{)) { theString.value = new StringValue(theString.value->data) ; He известно, как долго указатель, возращаемый этой функцией, будетнужен клиентам, поэтому объект StringValue не может использоваться совместно. theString.value->markUnshareable(); return &(theString.value->data[charlndex]); Большая часть этого кода является обшей для других функций - членов класса CharProxy, поэтому его следует инкапсулировать в закрытую функцию - член класса, которую будут вызывать все остальные функции. Второе отличие между символами char и заменяющими их объектами CharProxy проявляется, если имеется шаблон для массивов с подсчетом ссылок, который использует proxy-классы, чтобы различить вызов operator [ ] в качестве lvalue и rvalue: template<class Т> Массив со счетчиком ссылок, class Array { использующий proxy-объекты, public: class Proxy { public: Proxy(Array<T>& array, int index); Proxyb operator=(const T& rhs) ; operator TO const; const Proxy operator[] (int index) const; Proxy operator[] (int index); Рассмотрим, как могут использоваться эти массивы: Array<int> intArray; intArray[5] =22; Нормально. intArray[5] +=5; бшибка! ++intArray[5]; Ошибка! Как и ожидалось, использование функции operator [ ] в качестве цели простого присваивания завершается успехом, но если она расположена в левой части вызова функций operator+= или operator++, код будет выполнен некорректно. Это связано с тем, что функция operator [] возвращает proxy-объект, а для объектов Proxy не определены операторы operator+= или operator++. ArraY<Rational> array; Ho, оказывается, использовать эти массивы нельзя: cout array[4].numerator(); Ошибка! int denom = array[22].denominator(); Ошибка! Проблема была предсказуема: функция operator [ ] возвращает proxy-объект для действительного числа, а не настоящий объект Rational. Но функции-члены numerator и denominator существуют только для объектов типа Rat ional, а не для соответствующих proxy-объектов. Поэтому ваши компиляторы и жалуются. Чтобы proxy-объекты были более похожи на замещаемые ими объекты, вы должны перегрузить все функции, применимые к настоящим объектам. Другая ситуация, в которой proxy-объекты не могут заменить настоящие объекты, возникает при передаче функциям, принимающим ссылку на не-const объекты: void swap(char& а, char& b); String s = +C+ ; swap(s[0], s[l]); Переставляет значения a и b. Ошибка, должно быть С++ . Такая перестановка исправила бы ошибку, но она не будет компилироваться.. Функция String::operator [ ] возвращает объект типа CliarProxy, а функция swap требует, чтобы оба ее аргумента имели тип cliar&. Объект CliarProxy может быть неявно преобразован к типу char, но функции его преобразования в тип chars не предусмотрено. Кроме того, объект типа char, в который он может быть преобразован, нельзя связать с параметрами chars функции swap, поскольку данный объект - временный (это значение, возвращаемое функцией operator char) и, как объясняется в правиле 19, существуют веские причины Аналогичная ситуация возникает и в случае других операторов, требующих lvalue в качестве аргумента, включая operator * =, operator =, operator- и т. д. Если вы хотите, чтобы названные операторы работали с функциями operator [ ], возвращающими proxy-объекты, вы должны определить каждую из этих функций для класса Array<T>::Proxy, что потребует немалой работы. К сожалению, вам придется либо выполнить ее, либо обойтись без данных операторов. Похожая проблема возникает при вызове функций - членов класса для реальных объектов через proxy-объекты. Говоря прямо, вы не можете этого сделать. Например, предположим, что вы хотите работать с массивами действительных чисел, использующими подсчет ссылок. Можно было бы определить класс Rational, а затем использовать выше приведенный шаблон Array: class Rational { public: Rational (int numerator = 0, int denominator = 1) ; int numerator0 const; int denominator0 const;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |