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