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

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


public:

Т& operator[](int index);

const T& operator[](int index) const;

ArraylD operator[](int index);

const ArraylD operator[](int index) const;

Тогда становится допустимым следующее: Array2D<float> data(10, 20);

cout data[3][6]; Нормально.

В данном случае data [3 ] обозначает объект ArraylD, а вызов функции operator [ ] для этого объекта соответствует запятой в записи (3 , 6) в исходном двумерном массиве.

Пользователи класса Array 2D не обязательно должны знать о существовании класса ArraylD. Объекты последнего класса представляют собой одномерные массивы, которые не существуют с точки зрения клиентов класса Array 2D. Пользовательские приложения пишутся так, как будто они используют реальные двумерные массивы. Пользователей не касается, что для того, чтобы удовлетворить капризам языка С++, классы Array 2D должны быть синтаксически совместимы с одномерными массивами.

Каждый объект ArraylD обозначает одномерный массив, отсутствующий в абстрактной модели, применяемой пользователями класса Array 2D. Объекты, которые обозначают другие объекты, часто называются proxy-объектами (proxy objects), а классы, на основе которых создаются proxy-объекты, часто называются proxy-классами (proxy classes). В этом примере ArraylD является proxy-классом. Экземпляры объектов этого класса соответствуют одномерным массивам, которые теоретически не существуют. (Терминология proxy-классов и объектов не является универсальной; иногда объекты таких классов называются заместителями (surrogates).)

Различение записи и чтения в функции operator[]

Использование proxy-классов для реализации классов, экземпляры которых действуют как многомерные массивы, является общепринятым, но этим возможности proxy-классов не исчерпываются. Например, в правиле 5 показано, как с помощью таких классов не допустить, чтобы конструкторы с единственным аргументом выполняли ненужные преобразования типов. Но наиболее известным применением proxy-классов является различение чтения и записи в функции operator[].

Рассмотрим строковый тип с подсчетом ссылок, который поддерживает operator [ ]. Такой тип подробно описан в предыдущем разделе. Если вы не знакомы с понятиями, лежащими в основе подсчета ссылок, ознакомьтесь с правилом 29 сейчас.



Строковый тип, поддерживающий operator [ ], позволяет пользователям писать подобный код:

string si, s2; Строковый класс;

использование proxy-классов

не позволяет этому классу

соответствовать стандартному

интерфейсу строк, cout sl[5]; Считать si.

s2[5] = X ; Записать s2.

sl[3] = s2[8]; Записать si, считать s2.

Обратите внимание, что функция operator [ ] может вызываться как для чтения, так и для записи символа. При чтении оператор стоит справа от оператора присваивания, при записи - слева. Вообще говоря, использование объекта слева от оператора присваивания (как lvalue) означает, что он может изменяться, использование его справа (как rvalue) - что он не может быть изменен.

Так как реализация чтения, в особенности для структур данных со счетчиком ссылок, требует намного меньших усилий, чем реализация записи, хотелось бы различать использование функции operator [ ] слева и справа от оператора присваивания. Как объясняется в правиле 29, запись объектов со счетчиком ссылок может включать копирование всей структуры данных, а для чтения достаточно возврата значения. К несчастью, не существует способа определить контекст вызова operator [ ] внутри самой функции, то есть невозможно различить ее использование как lvalue и rvalue.

Постойте , - скажете вы, - нам не нужно делать этого. Мы можем перегрузить функцию operator [ ], воспользовавшись тем, что она объявлена как const, и это позволит нам различать чтение и запись . Другими словами, предлагается решить задачу таким образом:

class String { public:

const charSt operator[](int index) const; Для чтения.

charSc operator[] (int index) ; Для записи.

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

string si, s2;

cout sl[5]; Вызывает не-const operator[] ,

так как si не-const объект.

s2[5] = X ; Также вызывает не-const

operator[], так как s2 не-const объект.

sl[3]=s2[8]; В обоих случаях вызывается



не-const operator[], так как и si, и s2 - не-const объекты.

В таком случае перегрузка функции operator [ ] не позволяет различать запись и чтение.

В примере, иллюстрирующем правило 29, все вызовы operator [ ] выполнялись для записи. На этот раз поступим иначе. Различить использование operator [ ] слева и справа от оператора присваивания в самой функции нельзя, но, может быть, все-таки найдется способ обойти это ограничение?

Да, невозможно определить внутри самой функции operator [ ], используется ли она в контексте lvalue или rvalue, однако ничто не мешает по-разному обрабатывать чтение и запись, если отложить действия до тех пор, пока не будет видно, как используется результат функции operator [ ]. Все что нужно - отсрочить решение о том, будет ли выполняться чтение или запись объекта, до тех пор пока не произойдет возврат из вызова функции operator [ ]. (Это пример отложенной оценки - см. правило 17.)

Proxy-класс позволяет получить нужный выигрыш во времени, поскольку функцию operator [ ] можно изменить так, чтобы она возвращала proxy-объект для символа строки, а не сам символ. Затем надо подождать и посмотреть, как этот proxy-объект будет использоваться. Если он считывается, значит, вызов функции operator [ ] следует рассматривать как чтение, если записывается - как запись.

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

□ создавать, то есть определять, какой символ строки они заменяют;

□ использовать в качестве цели присваивания, при этом выполняется присваи вание символу строки, который они заменяют. В данном случае proxy-объект используется как lvalue;

□ использовать любым другим способом. При этом proxy-объект выступает как rvalue.

Вот определение для класса string с подсчетом ссылок, где распознавание функции operator [ ] (используется ли она как lvalue или rvalue) осуществляется с помощью proxy-класса:

class String { Строки с подсчетом ссылок;

public: подробнее см. правило 29.

class CharProxy { Proxy-объекты для символов.

public:

CharProxy{Strings str, int index); Создание. CharProxy& operator=(const CharProxy& rhs) ; CharProxySt operator=(char c); Как lvalue,

operator char() const; Как rvalue,

private:

StringSc theString; Строка, которой

принадлежит proxy-объект.

int charlndex; Символ в строке, который

заменяет proxy-объект.



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

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