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

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


Правило 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;



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

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